Designing a custom Relying Party Security Token Service (RP-STS)

Now that we have done most of the heavy lifting by creating the SAML v1.1 security token in the previous recipe, in this recipe we will showcase the steps to create an RP-STS that can validate the incoming security token and retrieve back the claims issued by the IP-STS.

Note

Note that to simplify our solution, we will be using the RP web application project to interchangeably represent the RP-STS. In a real time scenario, there will be different applications where the RP contacts the RP-STS for token validation.

How to do it...

First, we create a contract in the Common project and then use an ASP.NET web forms based RP to provide an implementation to retrieve the claims from the incoming security token. Follow these steps:

  1. Open the IdentityManagement solution created in the previous recipe. Create an abstract class SecureTokenConsumerBase under the Common project.
  2. Specify an abstract method GetTokenHandlerCollection to retrieve a SecurityTokenHandlerCollection instance.
  3. Specify a DeserializeToken abstract method to de-serialize the incoming token into a SecurityToken object. The method accepts the SecurityTokenHandlerCollection object as a parameter.
  4. Finally, create a virtual method ParseAttributesFromSecureToken to provide a default implementation for retrieving the claim key/value pairs from the SecurityToken:
    public abstract class SecureTokenConsumerBase
    {
    protected abstract SecurityTokenHandlerCollection GetTokenHandlerCollection();
    protected abstract SecurityToken DeserializeToken(SecurityTokenHandlerCollection handlers);
    public virtual Dictionary<string, string> ParseAttributesFromSecureToken()
    {
    Dictionary<string, string> attributes = new Dictionary<string, string>();
    var handlers = GetTokenHandlerCollection();
    var token = DeserializeToken(handlers);
    ClaimsIdentityCollection idc = handlers.ValidateToken(token);
    foreach (var claimsIdentity in idc)
    {
    foreach (var claim in claimsIdentity.Claims)
    {
    attributes.Add(claim.ClaimType, claim.Value);
    }
    }
    return attributes;
    }
    }
    
  5. Add a Visual Studio 2010 C# ASP.NET Web Application project to the IdentityManagement solution and name it WebRP. Add a reference to the System.IdentityModel, System.ServiceModel, Microsoft.IdentityModel, and the Common assemblies.
  6. Add a class file to the project and name it Saml11SecureTokenConsumer. Implement the SecureTokenConsumerBase abstract class.
  7. Implement the GetTokenHandlerCollection method to return a SecurityTokenHandlerCollection object that will be used to read and validate the token:
    protected override SecurityTokenHandlerCollection GetTokenHandlerCollection()
    {
    SecurityTokenHandlerCollection collection = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
    List<SecurityToken> serviceTokens = new List<SecurityToken>();
    X509Certificate2 encryptCert = CertificateUtil.GetCertificate(
    StoreName.My,
    StoreLocation.LocalMachine,
    ConfigurationManager.AppSettings["RpCertificate"]);
    serviceTokens.Add(new X509SecurityToken(encryptCert));
    SecurityTokenResolver serviceResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(serviceTokens.AsReadOnly(), false);
    collection.Configuration.ServiceTokenResolver = serviceResolver;
    X509CertificateStoreTokenResolver certificateStoreIssuerResolver = new X509CertificateStoreTokenResolver(StoreName.My, StoreLocation.LocalMachine);
    collection.Configuration.IssuerTokenResolver = certificateStoreIssuerResolver;
    return collection;
    }
    

    The certificate that is used to encrypt the token, defines a SecurityTokenResolver instance. The subject information is retrieved from the Web.config AppSettings key, RpCertificate.

  8. Implement the DeserializeToken method to read the incoming encrypted token XML string into a SecurityToken object using the handler:
    protected override SecurityToken DeserializeToken(SecurityTokenHandlerCollection handlers)
    {
    ServiceConfiguration config = new ServiceConfiguration(_serviceConfig);
    handlers.Configuration.AudienceRestriction = config.AudienceRestriction;
    handlers.Configuration.IssuerNameRegistry = config.IssuerNameRegistry;
    var txtReader = new StringReader(_token);
    StringBuilder sb = new StringBuilder();
    XmlReader reader = XmlReader.Create(txtReader);
    var token = handlers.ReadToken(reader);
    resolvedToken = SerializeToken(token as SamlSecurityToken);
    return token;
    }
    

    The service configuration name is set via the public constructor that accepts the token XML string and the configuration name as parameters.

    Note

    For illustration purposes, we are using a SerializeToken private helper method to serialize the decrypted token into an XML string to get it displayed on the page.

    The SerializeToken method uses the Saml11SecurityTokenHandler object to serialize the token:

    private string SerializeToken(SamlSecurityToken token)
    {
    var handler = new Saml11SecurityTokenHandler();
    XmlWriterSettings settings = new XmlWriterSettings()
    {
    Encoding = Encoding.UTF8,
    Indent = true
    };
    StringBuilder sb = new StringBuilder();
    var writer = XmlTextWriter.Create(sb, settings);
    writer.WriteProcessingInstruction("xml", "version="1.0" encoding="UTF-8" standalone="yes"");
    handler.WriteToken(writer, token);
    writer.Close();
    return sb.ToString();
    }
    
  9. Create the Microsoft.IdentityModel configuration in the Web.config file:
    <microsoft.identityModel>
    <service name="IdentityServiceConfig">
    <audienceUris>
    <add value="http://localhost:8002/Default.aspx"/>
    </audienceUris>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
    <trustedIssuers>
    <add name="STSTestCert" thumbprint="c0705376307238dbdcd8a85b266c4b0fd1ca0e3d"/>
    </trustedIssuers>
    </issuerNameRegistry>
    </service>
    </microsoft.identityModel>
    

    Specify the RP URL under the <audienceUris> element.

    Note

    Make sure to verify the certificate thumbprint specified under the<trustedIssuers> element. An incorrect thumbprint will result in a ID4175: The issuer of the security token was not recognized by the IssuerNameRegistry exception.

  10. In the Default.aspx.cs file, override the CreateChildControls method to create an instance of the Saml11SecureTokenConsumer class and display the claims in the page retrieved using the ParseAttributesFromSecureToken method of the Saml11SecureTokenConsumer class:
    protected override void CreateChildControls()
    {
    base.CreateChildControls();
    var rv = Request.Params["wresult"];
    Saml11SecureTokenConsumer tokenConsumer = new Saml11SecureTokenConsumer(Server.HtmlDecode(rv), "IdentityServiceConfig");
    _claimList = tokenConsumer.ParseAttributesFromSecureToken();
    HtmlTable table = new HtmlTable();
    table.Border = 1;
    foreach (var item in _claimList)
    {
    HtmlTableRow row = new HtmlTableRow();
    HtmlTableCell cell1 = new HtmlTableCell();
    cell1.InnerText = item.Key;
    HtmlTableCell cell2 = new HtmlTableCell();
    cell2.InnerText = item.Value;
    row.Controls.Add(cell1);
    row.Controls.Add(cell2);
    table.Controls.Add(row);
    }
    this.form1.Controls.Add(table);
    HtmlTextArea saml = new HtmlTextArea();
    saml.Value = tokenConsumer.ResolvedToken;
    saml.Rows = 40;
    saml.Cols = 120;
    this.form1.Controls.Add(saml);
    }
    
  11. Open the StsProcessing.aspx page under the WebSTS project and include the following script to allow the page to perform an automatic post:
    <script type="text/javascript">
    setTimeout('document.forms[0].submit()', 0);
    </script>
    
  12. Compile the solution. Run the WebSTS application. You will be redirected to the Default.aspx page of the WebRP application and if the token resolves correct, the retrieved claims will be displayed in the page along with the decrypted SAML 1.1 token (see the following screenshot):
    How to do it...

How it works...

In case of the RP-STS, the process is just the reverse of what we did for generating a token in the previous recipe. In this case, we de-serialize the XML string into a SecurityToken instance using the token handler and then the claims are retrieved from the token. There are, however, a few important steps to note in the process. First, a SecurityTokenResolver instance is created using the RP certificate and is set as the service token resolver (ServiceTokenResolver property) on the default SecurityTokenHandlerCollection object. The certificate location is also set for the issuer using the IssuerTokenResolver property. The issuer will get validated by using the IssuerNameRegistry information set in the Microsoft.IdentityModel configuration section and the store location set in the IssuerTokenResolver property to find the resolver certificate. The SecurityTokenHandlerCollection instance is then used to validate the incoming token and return the collection of claim attributes using the ValidateToken method, as seen in the default ParseAttributesFromSecureToken implementation in the SecureTokenConsumerBase abstract class.

There's more...

The ability to gain control over the process of generating and consuming SAML tokens is a powerful idea. Outside the Microsoft ecosystem, this ability allows you to build interoperable claims-based identity enabled applications by removing the dependency on Federation Metadata. For example, you can now request resources from a JSP application by posting the SAML 1.1 token to the target Java-based RP-STS, using HTTP POST.

See also

The complete source code for this recipe can be found in the Chapter 3Recipe 1-2-3 folder.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.142.166