Part2: ACS Federation with Custom STS
In yesterday’s post I talked about ACS and how to interact with it as a standard web service, providing claims transformation functionality. Today I will show how we can federate ACS with our custom STS written using Geneva Framework. This architecture opens up many interesting scenarios. Here are the major steps involved in this architecture.
-
We need to establish a trust relationship between our local STS and Access Control Service (ACS). We can do this using ACS web portal. This is essentially just exchange of certificate(s)
-
I have already created a simple STS, which runs on the same machine as my client, so it will issue tokens to any caller (anonymous or authenticated).
-
Once I got token from my local STS, I will pass this token to ACS, which will use the claims contained in this token to derive its transformation logic and will return a new token with different claims.
-
I can now present this new token to any web service or web site (which trust ACS) and get my job done.
Here is simple code, which first gets token from local STS and then uses it to get a new token from ACS.
private static void GetTokenFromACS Using SAMLToken GeneratedBy MyCustomSTS()
{
// This is using WSFed binding - so WCF is doing it's magic of intermediate token acquistion.
var binding = new CustomBinding("IssuedTokenForCertificate");
var acsCert = GetACSCertificate();
var identity = new X509CertificateEndpointIdentity(acsCert);
var epa = new EndpointAddress(new Uri("http://accesscontrol.windows.net/sts/eval01/saml for certificate"), identity);
var trustVersion = TrustVersion.WSTrust13;
var clientCredentials = new ClientCredentials();
WSTrustClient client = new WSTrustClient(binding, epa, trustVersion, clientCredentials);
RequestSecurityToken rst = new RequestSecurityToken(RequestTypeConstants.Issue, KeyTypeConstants.Symmetric);
try
{
RequestSecurityTokenResponse rstr;
var token = client.Issue(rst, out rstr) as GenericXmlSecurityToken;
var rpCert = new X509Certificate2("zamdnetprivatekeycert.pfx", "a");
var txtStat = ExtractSAMLAssertion(token, rpCert);
}
catch (Exception exp)
{
Console.WriteLine(exp.ToString());
}
}
One important difference from yesterday's post is: we are not using ACS solution credentials here at all (solution userId, password etc). Why? Because we are authenticating with our local STS, and ACS simply trust the token issued by our local STS. With the architecture, we can easily go and change our authentication mechansim with our local STS (go from Annonymous to kerberos) without effecting ACS.
This is how the binding configuration look like:
<binding name="IssuedTokenForCertificate">
<security authenticationMode="IssuedTokenForCertificate"
messageSecurityVersion="WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10"
requireSignatureConfirmation="false">
<issuedTokenParameters keyType="SymmetricKey" tokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
<claimTypeRequirements>
<add claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
</claimTypeRequirements>
<issuer address="http://localhost:9000/STS" binding="wsHttpBinding"
bindingConfiguration="AnnonyForCertificate">
<identity>
<certificate encodedValue=" +7y1Gj9Ta2w==" />
</identity>
</issuer>
<issuerMetadata address="http://localhost:9000/STS/mex" />
</issuedTokenParameters>
<secureConversationBootstrap />
</security>
<httpTransport />
</binding>
So you can see from the above that we are talking to our local STS using wsHttpBinding configured to allow anonymous access.
<binding name="AnnonyForCertificate">
<security mode="Message">
<message clientCredentialType="None" negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
By decrypting the token I can see following assertions returned by ACS.
<saml:AttributeStatement xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">
<saml:Subject>
<saml:SubjectConfirmation>
<saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:holder-of-key</saml:ConfirmationMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
</e:EncryptionMethod>
<KeyInfo>
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Root Agency</X509IssuerName>
<X509SerialNumber>53391460269745460375752793295988585963</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>mhZrKiIRoUyuwy8yXCMWq/xe3H8YyZOh4wMSMh3FknjIamC7QLBhiQAfV1DvQXNJhlY=</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</KeyInfo>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Attribute AttributeName="action" AttributeNamespace="http://docs.oasis-open.org/wsfed/authorization/200706/claims">
<saml:AttributeValue>that's done</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
Now let me show you how I have established the trust relationship between my STS & ACS and how I configured ACS to return single claim in returned SAML token.
So the above rule says: If you get a token issued by zamd.net (which is my local STS) and it contains a Name claim with the value of Zulfiqar. Please send back an Action claim with the value of “that’s done”. Because that’s the only rule dealing with my local STS that’s why the returned token has only one claim in it.
And finally this is how I established a trust relationship between my STS and ACS. I added a new issuer using the Portal, uploading my STSs public key certifcate to ACS and that’s it.
Now with this certificate, ACS can correctly validate that incomming token is indeed issued by my local STS(because STS signs the issued token), and then runs the transformation logic to choose correct set of claims to return back.
In this post, all the funcationality of going to the local STS, getting token and attaching it to the Issue call made to ACS is done by WSFederationHttpBinding. In the next post,I will show a more explicit way of achieving the same result, so that you can have control over the token issued by your local STS, you can cache etc, and then forward it to ACS on your will. Stay tuned...