Creating a Basic STS using Geneva Framework( aka Zermatt)
Zermatt (Geneva Framework) is an identity framework built on top of solid WCF foundation. It makes writing custom STSs a breeze as the amount of code you have to write is substantially low compared with, when building the same STS directly on top of WCF. In this post I will show you how to write a basic STS using Zermatt.
This STS will be used by active clients, which mean, it will expose its token issuance functionality using the WS-Trust protocol. In a later post I will talk about passive clients and WS-Federation protocol.
So the first step is to drive a class from Zermatt provided SecurityTokenService abstract base class. This class provides most of the plubming required implementing an STS and you have to only provide missing pieces in your derived class.
class CustomSecurityTokenService : SecurityTokenService{}
Next you have to override following two methods:
protected override Scope GetScope(IClaimsPrincipal principal, RequestSecurityToken request)
{
Scope scope = base.GetScope(principal, request);
scope.EncryptingCredentials = new X509EncryptingCredentials(CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=localhost"));
return scope;
}
public override ClaimsIdentityCollection GetOutputSubjects(Scope scope, IClaimsPrincipal principal, RequestSecurityToken request)
{
ClaimsIdentityCollection collection = new ClaimsIdentityCollection();
collection.Add( new ClaimsIdentity( new Claim( ClaimTypes.Name, "zulfiqar" ) ) );
return collection;
}
In the GetScope method, you get all the information about the requestor and based on that you can select credentials which will be used to encrypt the issued token to the requestor. You can do that in the GetOutputSubjects method as well in which case you don’t need to override GetScope.
In GetOutputSubjects method you create a collection of claims which will be added to the issued token. In a production STS, this is where you will access the external system to get information about the requestor and will create claims based on that information. In this example, I’m simply returning a single Name claim. That’s it - you are done with STS part.
Now as an STS is essentially a service, so you need to host it inside a ServiceHost and expose it’s functionality using an Endpoint (Standard WCF stuff). Zermatt also provides a custom service host, known as WSTrustServiceHost – which adds Zermatt specific stuff (configuration etc) to the standard ServiceHost class.
SecurityTokenServiceConfiguration config =
new SecurityTokenServiceConfiguration(stsAddress, new X509SigningCredentials(stsCert));
stsConfig.SecurityTokenService = typeof(CustomSecurityTokenService);
//Create host based on our configuration.
stsHost = new WSTrustServiceHost(stsConfig, new Uri(stsAddress));
// Add an endpoint - this can be added using standard config mechansims as well.
stsHost.AddServiceEndpoint(typeof(IWSTrustFeb2005SyncContract),
new WSHttpBinding(), new Uri(stsAddress));
// open the host now.
stsHost.Open();
At this point our STS is ready to go. You might have noticed that I’m using the standard WSHttpBinding, which mean my STS will authenticate requestors using windows authenticaiton (default for WSHttpBinding) – and then I can use all that information to inlcude appropraite claims in the returned SAML token. One last important point is that Zermatt is build on top of WCF security model – so I can change the binding to require a certificate or a userName to issue a SAML token.
In the next post I will show how to a write a basic test harnesses for our Security Token Service.