If you haven’t checked out the .NET Access Control Service yet – I can highly recommend it!
Justin did two talks about it at PDC:
In the 2nd talk Justin showed how to use and process claims coming from the ACS rules engine in your own services. You can find this code in the “CardSpace Calculator” sample in the ACS SDK.
The sample uses the “old” WCF plumbing to process tokens and create claims based on that. I wanted to find out what has to be done to migrate the sample to use Geneva.
First let’s have a look at what you want to accomplish with such a scenario:
- your clients live in a different trust domain as your service
- to federate these two domains, you do the following
- register your client’s identity provider (Live ID, Geneva Server/Framework, other WS-Trust 1.3 compat STS) at the ACS
- your client obtains a token from the ACS (by sending their identity token)
- this token is used to authenticate with your service
- your service accepts tokens from the ACS and uses their claims for identity related work
For your service this means the following:
- accept tokens that are signed by the ACS
- make sure the issuer of that token is your personal instance of the ACS (http://accesscontrol.windows.net/yoursolution)
In Geneva terms this boils down to the following pieces of plumbing:
- issuer name registry that knows about the ACS issuer certificate
- security token handler that check the SAML issuer name
There are two ways to accomplish this. Either you use the standard ConfigurationBasedIssuerNameRegistry and add the ACS issuer thumbprint to it:
Or you write a custom registry that has the ACS issuer baked in:
class AccessControlServiceIssuerRegistry : IssuerNameRegistry
public override string GetIssuerName(SecurityToken securityToken)
X509SecurityToken token = securityToken as X509SecurityToken;
if (token == null)
throw new SecurityTokenException(“Token is not a X509 Security Token”);
var cert = token.Certificate;
if (cert.Thumbprint.Equals(“416E6FA5D982B096931FBF42C4A3DCD608856C95”, StringComparison.OrdinalIgnoreCase))
throw new SecurityTokenException(“Token not issued by access control service”);
Security Token Handler
Once you trust the issuer, you also have to make sure that the token got issued by your personal instance of the ACS. This is done by checking the SAML issuer name information in the token. This is the job of a custom security token handler – you can simply derive from the existing Saml11SecurityTokenHandler and inject this logic in the ValidateToken method.
class AccessControlServiceSaml11SecurityTokenHandler : Saml11SecurityTokenHandler
public AccessControlServiceSaml11SecurityTokenHandler(string solution)
_solution = solution;
public override ClaimsIdentityCollection ValidateToken(SecurityToken token)
var identities = base.ValidateToken(token);
“http://accesscontrol.windows.net/” + _solution);
Bringing the pieces together
You can now wire up the two custom components using the FederatedServiceCredentials class like this:
ServiceHost host = new ServiceHost(typeof(CalculatorService));
var handlers = new SecurityTokenHandlerCollection();
Another option would be to use the configuration file – but in this case we somehow have to transfer the solution name to the security token handler. Geneva security token handlers have a standard configuration extensibility hook that we can use to accomplish this (not very obvious at the beginning – but makes sense ;).
Let’s say our configuration should look like this:
<issuerNameRegistry type=“LeastPrivilege.AccessControlServiceIssuerRegistry, Service“ />
<add type=“LeastPrivilege.AccessControlServiceSaml11SecurityTokenHandler, Service“>
<accessControlServiceTokenRequirement solutionName=“leastprivilege“ />
To make the token handler aware of this sub config element, you have to add a new constructor to the token handler like this:
public AccessControlServiceSaml11SecurityTokenHandler(XmlElement customConfigElement)
if (customConfigElement.LocalName != “accessControlServiceTokenRequirement”)
throw new InvalidOperationException(“accessControlServiceTokenRequirement expected”);
var solutionAttr = customConfigElement.Attributes[“solutionName”];
if (solutionAttr == null)
throw new InvalidOperationException(“solution name expected”);
_solution = solutionAttr.Value;
(if you also need support for the samlSecurityTokenRequirement element, you can manually call LoadSamlTokenAuthenticatorRequirement on the SecurityTokenHandlerElement class).
Here you can find the complete code – to get it working I’d suggest you first get the original SDK sample up and running. After that you have to make the necessary adjustments in my code (solution name, certificates and URIs).