Couple of months ago Ezequiel posted a summary of a very interesting article published on the Identity issue of the Architecture Journal. This article talked about different patterns on the federated identity world. Last week we had an interesting requirement to solve in a project and this article came to my mind. Specifically one of the scenarios this article points out is when you don’t want the consumer to have knowledge about the relying party STS or you don’t want the client to “see” the token from that STS. The figure below illustrates this scenario. In this diagram, the relying party (service) will receive a token from STS A and will call the STS B to obtain the token.
This might be helpful when you can’t change the consumer side of the equation. This lead me to introduce the federated blog engine and how we leveraged this in that project…
The Federated Blog Engine
Let’s say we want to implement a blog engine where users can access through federated identity. My blog engine supports the passive profile federation, so when someone access it will get redirected to its IP STS, get authenticated, post the token to the website and voila! We can create new blog posts under our organization identity.
So far nothing new here. Now let’s say that we want to be able to post through Windows Live Writer. This is a rich client that runs on your desktop that you can’t change. On the other side the blog engine will have to expose the MetaWeblog API. Every operation of this API receives the user and password like the code below:
string NewPost(string blogId, string username, string password, Post post, bool publish);
In this case, the token on the client is a UsernameToken (not the WS-Security one, but think about the analogy). We will send that token (through Https) to the remote MetaWeblog API.
NewPost("mwoloski", "DOMAIN\matias", "mypassword", post, true)
When the message gets to the service we can intercept it and call the STS that will validate the credentials and will issue a SAML token for the service. The following piece of code shows how to make such request to a Geneva Server Beta 2 using the UserNameMixed endpoint. This endpoint will authenticate the user against AD.
private static ClaimsIdentityCollection GetToken(string username, string password, Uri stsUrl, X509Certificate2 signatureCertificate, Uri relyingPartyIdentifier, X509Certificate2 decryptingCertificate)
var binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential, false);
binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
binding.Security.Message.EstablishSecurityContext = false;
var credentials = new ClientCredentials();
credentials.UserName.UserName = username;
credentials.UserName.Password = password;
var client = new WSTrustClient(binding, new EndpointAddress(stsUrl), TrustVersion.WSTrust13, credentials);
var request = new RequestSecurityToken();
request.RequestType = "http://schemas.microsoft.com/idfx/requesttype/issue";
request.AppliesTo = new EndpointAddress(relyingPartyIdentifier);
var token = client.Issue(request) as GenericXmlSecurityToken;
var claims = token.ToClaimsIdentityCollection(TrustVersion.WSTrust13, signatureCertificate, decryptingCertificate);
var token = GetToken("matias",
CertificateUtility.GetCertificate(StoreName.TrustedPeople, StoreLocation.LocalMachine, "CN=Geneva Signing Certificate..."),
CertificateUtility.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=hostname"))