The case of the missing ‘Dialect’ (Part 4)

This is the fourth in a series of posts [part1, part2,part3] where I describe some issues regarding the usage of claims requirements on the WCF platform.

In the previous two posts, I presented some information regarding the BizTalk Identity Services STS.

In this post I will show how to build a minimalistic WCF service that relies on this STS for the authorization decisions. I will also show how to build a client that uses this service.

For the sake of clarity, I will use no configuration file, so everything will be done in code. 

Service

The service’s endpoint uses a WSFederationHttpBinding, configured as follows

   1: WSFederationHttpBinding binding = new WSFederationHttpBinding();
   2: binding.Security.Message.IssuedKeyType = SecurityKeyType.SymmetricKey;
   3: binding.Security.Message.IssuedTokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";
   4: binding.Security.Message.IssuerAddress = new EndpointAddress("http://identity.biztalk.net/sts/<username>/issued_for_certificate");
   5: binding.Security.Message.IssuerMetadataAddress = new EndpointAddress("http://identity.biztalk.net/sts/<username>/mex");
  • The IssuedKeyType property is set to SecurityKeyType.SymmetricKey. This means that the proof-of-possession key will be a symmetric key (see WS-Trust for details). It also means that the STS must have the certificate of the service, in order to encipher the proof-of-possession key (see below for how to configure the STS with this certificate).
  • The IssuedTokenType property reflects the type of token issued by the STS (a version 1.1 SAML token).
  • The IssuerAddress property points to the STS endpoint that issues the required tokens. In this case, it’s the endpoint with name ‘SelfSignedSamlForCertificate’ (see part3), that accepts self-issued SAML tokens. 
  • The IssuerMetadataAddress property points to the address of the STS metadata.

Why is the IssuerAddress necessary? Namely, isn’t the IssuerMetadataAddress enough? Because, as seen in the previous part, this metadata contains several endpoints that implement the STS contract. By specifying the required endpoint’s address, this ambiguity is solved.

The previous settings define the required token type but not the required claims types. However, the desired claims dialect (“http://schemas.xmlsoap.org/ws/2006/12/authorization/authclaims“) is not directly supported by WCF, so the ClaimTypeRequirement property cannot be used. For this purpose, this requirement must be directly configured in the TokenRequestParameters collection.

   1: XNamespace wst = "http://schemas.xmlsoap.org/ws/2005/02/trust";
   2: XNamespace wsf = "http://schemas.xmlsoap.org/ws/2006/12/authorization";
   3: var claims = new XElement(wst + "Claims",
   4:    new XAttribute("Dialect", "http://schemas.xmlsoap.org/ws/2006/12/authorization/authclaims"),
   5:    new XElement(wsf + "ClaimType",
   6:       new XAttribute("Uri", "http://schemas.xmlsoap.org/ws/2006/12/authorization/claims/action"),
   7:       new XElement(wsf + "Value")
   8:    )
   9: );
  10: var xdoc = new XmlDocument();
  11: xdoc.LoadXml(claims.ToString());
  12: binding.Security.Message.TokenRequestParameters.Add(xdoc.DocumentElement);

Finally, I create a service host, add an endpoint with this binding, set the service’s certificate and enable metadata retrieval.

   1: ServiceHost sh = new ServiceHost(typeof(ServiceImpl), new Uri("http://<servicehost>:8080/saac"));
   2: sh.AddServiceEndpoint(typeof(IService), binding, "ep1");
   3: // Set the service's certificate
   4: sh.Credentials.ServiceCertificate.SetCertificate("cn=<servicehost>");
   5: // Enable metadata retrieval
   6: ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
   7: smb.HttpGetUrl = new Uri("http://<servicehost>:8080/saac/md");
   8: smb.HttpGetEnabled = true;
   9: sh.Description.Behaviors.Add(smb);
  10: sh.Open();

The <servicehost> string should be replace by the host name.

BizTalk Identity Services configuration

The following configurations are required at the BizTalk Identity Services, after login under the <username> account:

  • Manage Access Control/Relying Party: add the address of the service’s endpoint’s (http://<servicehost>:8080/saac/ep1) and the service’s certificate. This association between an address and a certificate is necessary because the STS needs the public key of the relying service to encrypt the proof-of-possession token.
  • Manage Access Control/Rules: define a claim mapping with:

Client

At the client side, I use the MetadataResolver class to dynamically build the binding and address to the service, instead of statically using the svcutil.exe tool. Just to simply the example. the interface with the service contract is shared between the service and the client.

   1: ServiceEndpointCollection sec = MetadataResolver.Resolve(
   2:    typeof(IService),
   3:    new Uri("http://<servicehost>:8080/saac/md"),
   4:    MetadataExchangeClientMode.HttpGet);
   5: ServiceEndpoint sep = sec[0];
   6: WSFederationHttpBinding clientBinding = sep.Binding as WSFederationHttpBinding;

Then I create a ChannelFactory<T>, using the resolved binding and address, set the client certificate authentication details (PeerTrust because the service’s certificate is “home made”), create a binding and call the service’s operation.

   1: ChannelFactory<IService> cf = new ChannelFactory<IService>(clientBinding, sep.Address);
   2: cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
   3: cf.Credentials.ServiceCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
   4: IService ch = cf.CreateChannel();
   5: Console.WriteLine(ch.Operation("hello"));

Running the scenario

When I first ran this scenario, the CardSpace UI popped up, because the STS requires a SAML token but does not point to the metadata of its issuer. Then I selected the card that I registered in BizTalk Services, waited a couple of seconds for the client to request the authorization token and got a “ID3037: The specified request failed.” fault exception.

Why? Well, that’s the subject of the next post of this series.

Advertisements

One thought on “The case of the missing ‘Dialect’ (Part 4)

  1. Pingback: The case of the missing ‘Dialect’ (epilogue) « Pedro Félix’s shared memory

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s