WIF, ADFS 2 and WCF–Part 6: Chaining multiple Token Services

See the previous posts first.

So far we looked at the (simpler) scenario where a client acquires a token from an identity provider and uses that for authentication against a relying party WCF service.

Another common scenario is, that the client first requests a token from an identity provider, and then uses this token to request a new token from a Resource STS or a partner’s federation gateway.

This sounds complicated, but is actually very easy to achieve using WIF’s WS-Trust client support. The sequence is like this:

  1. Request a token from an identity provider. You use some “bootstrap” credential for that like Windows integrated, UserName or a client certificate. The realm used for this request is the identifier of the Resource STS/federation gateway.
  2. Use the resulting token to request a new token from the Resource STS/federation gateway. The realm for this request would be the ultimate service you want to talk to.
  3. Use this resulting token to authenticate against the ultimate service.

Step 1 is very much the same as the code I have shown in the last post. In the following snippet, I use a client certificate to get a token from my STS:

private static SecurityToken GetIdPToken()
{
   
var factory = new WSTrustChannelFactory
(
       
new CertificateWSTrustBinding(SecurityMode
.TransportWithMessageCredential,
        idpEndpoint);
    factory.TrustVersion =
TrustVersion
.WSTrust13;
 
    factory.Credentials.ClientCertificate.SetCertificate(
       
StoreLocation
.CurrentUser,
       
StoreName
.My,
       
X509FindType
.FindBySubjectDistinguishedName,
       
“CN=Client”
);
 
   
var rst = new RequestSecurityToken
    {
        RequestType =
RequestTypes
.Issue,
        AppliesTo =
new EndpointAddress
(rstsRealm),
        KeyType =
KeyTypes
.Symmetric
    };
 
   
var
channel = factory.CreateChannel();
   
return channel.Issue(rst);
}

To use a token to request another token is slightly different. First the IssuedTokenWSTrustBinding is used and second the channel factory extension methods are used to send the identity provider token to the Resource STS:

private static SecurityToken GetRSTSToken(SecurityToken idpToken)
{
   
var binding = new IssuedTokenWSTrustBinding
();
    binding.SecurityMode =
SecurityMode
.TransportWithMessageCredential;
 
   
var factory = new WSTrustChannelFactory
(
        binding,
        rstsEndpoint);
    factory.TrustVersion =
TrustVersion
.WSTrust13;
    factory.Credentials.SupportInteractive =
false
;
 
   
var rst = new RequestSecurityToken
    {
        RequestType =
RequestTypes
.Issue,
        AppliesTo =
new EndpointAddress
(svcRealm),
        KeyType =
KeyTypes
.Symmetric
    };
 
    factory.ConfigureChannelFactory();
   
var
channel = factory.CreateChannelWithIssuedToken(idpToken);
   
return
channel.Issue(rst);
}

For this particular case I chose an ADFS endpoint for issued token authentication (see part 1 for more background). Calling the service now works exactly like I described in my last post.

You may now wonder if the same thing can be also achieved using configuration only – absolutely. But there are some gotchas. First of all the configuration files becomes quite complex. As we discussed in part 4, the bindings must be nested for WCF to unwind the token call-stack. But in this case svcutil cannot resolve the first hop since it cannot use metadata to inspect the identity provider. This binding must be supplied manually.

The other issue is around the value for the realm/appliesTo when requesting a token for the R-STS. Using the manual approach you have full control over that parameter and you can simply use the R-STS issuer URI. Using the configuration approach, the exact address of the R-STS endpoint will be used. This means that you may have to register multiple R-STS endpoints in the identity provider. Another issue you will run into is, that ADFS does only accepts its configured issuer URI as a known realm by default. You’d have to manually add more audience URIs for the specific endpoints using the ADFS Powershell commandlets.

I prefer the “manual” approach.

That’s it. Hope this is useful information.

This entry was posted in Uncategorized. Bookmark the permalink.

6 Responses to WIF, ADFS 2 and WCF–Part 6: Chaining multiple Token Services

  1. Mark Shyn says:

    Dominick,

    The code samples you provide in part 6 of your series on WIF/ADFS/WCF when chaining multiple STS’s are very helpful. But I have a question. If I understand you correctly, you are describing 3 security realms: (1) the realm in which the Identity Provider resides, (2) a service realm where the resource (application) resides, and (3) an R-STS realm where the STS closest to the application resides. Wouldn’t the last two realms normally be one and the same? It was my understanding that the application (e.g. web service) expects to receive a token that has been generated by an STS within its own security realm. Can you please clarify? I suspect that you are describing a more general scenario where a federation gateway is able to generate tokens for one or more services (i.e. applications) each of which may resides in a distinct security realm.

    Mark S.

  2. Wendell Gruber says:

    I think I’m trying to do this exact same thing but in the newer version of WIF and by using a federated bootstrap but it isn’t working. Any ideas?

    WindowsIdentity wi = WindowsIdentity.GetCurrent();
    BootstrapContext bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as BootstrapContext;
    SecurityToken bootstrapToken = bootstrapContext.SecurityToken;

    var binding = new IssuedTokenWSTrustBinding();
    binding.SecurityMode = SecurityMode.TransportWithMessageCredential;
    var factory = new WSTrustChannelFactory(
    binding,
    “https://adfsserver.hallmark.com/adfs/services/trust/13/issuedtokenmixedsymmetricbasic256/”);
    factory.TrustVersion = TrustVersion.WSTrust13;
    factory.Credentials.SupportInteractive = false;

    var rst = new RequestSecurityToken
    {
    RequestType = WSTrust13Constants.RequestTypes.Issue,
    AppliesTo = new EndpointReference(“https://adfsserver.hallmark.com/adfs/services/trust”), //this matches the rp in adfs for our test
    KeyType = WSTrust13Constants.KeyTypes.Bearer,
    TokenType = “urn:oasis:names:tc:SAML:2.0:assertion”,
    ActAs = new SecurityTokenElement(bootstrapToken),
    };

    //var channel = factory.CreateChannelWithIssuedToken(bootstrapToken);
    //var channel = factory.CreateChannelWithActAsToken(bootstrapToken);
    var channel = factory.CreateChannel();
    var myResult = channel.Issue(rst);

  3. Dominick,

    Do you know why it’s not possible to use a bearer token from the IdP STS to let ADFS issue a new token? All issuedtoken endpoint require a symmetric or asymmetric key in the IdP token.

    Or am I missing something?

    Thanks,
    — Marcel

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 )

Facebook photo

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

Connecting to %s