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.