Access Control Service - Part1
.Net Services include a service known as “Access Control Service”, which is a cloud based Security Token Service (STS).
ACS supports both active (WS-Trust) and passive (WS-Federation) clients. In this post I will talk about WS-Trust support in ACS.
Basic function of any STS is to accepts a token and return a different token essentially doing claims transformation. This claims transformation is also at the core of ACS. It use a very flexible rule based transformation engine to map input claims into output claims. I have already signed up for .NET Services account and also downloaded & installed the SDK which includes some useful end-2-end samples. I will start with individual pieces and then try to connect them together in step by step manner.
Ok let’s see how we can talk to ACS just like another web service? ACS token issuance functionality is available using many different bindings (you can easily get this list by using “Add Service Reference” command or svcutil.exe).
Now thinking in terms of WCF: we already got a service, which is accessible over internet, so we need a WCF proxy to call it.
Geneva Framework introduced a new class known as WSTrustClient – As the name suggest, this class is a WCF proxy for WS-Trust contract. If you can’t use Geneva Framework then this same functionality is also provided by WCF. Please see my other post on IssuedSecurityTokenProvider. In this post – I’m going to use Geneva Framework because that’s the recommended approach for this scenario. Here is the code we can use to invoke ACS.
private static void AcquireTokenFromACSUsingUserName()
{
var binding = new WS2007HttpBinding("userNameForCert");
//ACS(STS) signing certificate...
//Replace following string with actual ACS cert string
var certData = Convert.FromBase64String("AwAAAAEOoik=");
var acsCert = new X509Certificate2(certData);
var identity = new X509CertificateEndpointIdentity(acsCert);
var epa = new EndpointAddress(new Uri("http://accesscontrol.windows.net/sts/eval01/username for certificate"), identity);
var trustVersion = TrustVersion.WSTrust13;
var clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = "your solution userName";
clientCredentials.UserName.Password = "your solution password";
WSTrustClient client = new WSTrustClient(binding, epa, trustVersion, clientCredentials);
RequestSecurityToken rst = new RequestSecurityToken(RequestTypeConstants.Issue, KeyTypeConstants.Symmetric);
rst.AppliesTo = new EndpointAddress("http://zamd.net");
RequestSecurityTokenResponse rstr;
// Send token issuance request to ACS.
var samltok = client.Issue(rst, out rstr);
}
By running above – I get a token back from ACS.
Now Because I own the certificate for relying part (zamd.net) – I have written some code to decrypt the issued token to see the returned claims. Here is that code:
var rpCert = new X509Certificate2("zamdnetprivatekeycert.pfx", "a");
var token = samltoken as GenericXmlSecurityToken;
var txtStat = ExtractSAMLAssertion(token, rpCert);
Following SAML assertion is returned in the response token.
<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>m7738wsrTe1GVNHl3wl1QEq4AJBERZY7GI7MMlWqRZBjM8T2UbrtK7M40+2JEm0JpgDMnVoq8Zhc8V5ui20lHKhAw2fOYSUBU529ZBzqKbYqDvIGApTXlp9ZVTvqS+Xo2kM315XtURkPHqmWvMA2eUUdZjA9tC+pvulWcuG2N4E=</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</KeyInfo>
</saml:SubjectConfirmation>
</saml:Subject>
<saml:Attribute AttributeName="username" AttributeNamespace="http://schemas.microsoft.com/strata/2008/09/accesscontrol/claims">
<saml:AttributeValue>Eval01</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="action" AttributeNamespace="http://docs.oasis-open.org/wsfed/authorization/200706/claims">
<saml:AttributeValue>Eval01Confirmed</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml:AttributeValue>Zulfiqar Ahmed (Orignal)</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="upn" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml:AttributeValue>Eval01</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute AttributeName="name" AttributeNamespace="http://schemas.xmlsoap.org/ws/2005/05/identity/claims">
<saml:AttributeValue>Eval01</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
So how does ACS decided to include these and only these claims in the returned token? Becase I have configured it using the portal- my portal configuration looks like following:
Finally here is WCF binding used to talk to ACS.
<bindings>
<ws2007HttpBinding>
<binding name="userNameForCert">
<security mode="Message">
<message clientCredentialType="UserName" negotiateServiceCredential="false"
establishSecurityContext="false" />
</security>
</binding>
</ws2007HttpBinding>
</bindings>