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:
- 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.
- 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.
- 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.
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.
I think I found the answer to my question — you were describing a more general scenario such as when using Microsoft’s Federation Gateway to access services deployed via Windows Azure.
Not sure I understand. Every “token receiver” has a realm. That’s the way it is.
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);
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
I can’t remember the details – but I think you are right.