PDC09 and the last 3 months…
December 9th, 2009
Wow, 3 months since my last post… Lots of things happened. We’ve been working with James Conard’s team from Microsoft
DPE on the PDC09 keynote demos, specifically the Platform Converge demo (Doug Pourdy) and the VS2010, AppFabric, NET4, WIF demo (by Cameron Skinner, read more in his post). We also helped delivering the training kits (identity, vs2010, azure, etc.) and the labs that were available on PDC. Tim and Johnny posted more details about this.
Being part of the making of a PDC keynote was very interesting. We had meetings with Partner Architects and Distinguished Engineers of the different Microsoft product groups. You get to learn a lot about politics in those meetings ;).
Once again we worked closely with Vittorio on Windows Identity Foundation content which was RTMed. David and Ryan on Azure and Jonathan Carter on Tailspin among other things. Finally, thanks all the team @ Southworks for the great support pre-PDC.
On a related note, the last month we worked hard with Eugenio, Erwin, the team at patterns & practices, Fede Boerr, Keith, Dominick to deliver a printed preview of the Claims Based Identity & Access Control Guide. Limited copies were distributed on the WIF booth and p&p booth at PDC and the book was very well received. We’ll be soon reaching a milestone and publish the following chapters:
Well, I had to catch-up with the blog… hopefully will keep the pace now.
Claims based Authentication & Authorization: The Guide
August 15th, 2009
Eugenio announced yesterday the kickoff of a new guide from patterns & practices in which I’m collaborating: Claims based Authentication & Authorization Guide.
This is not a new topic as Eugenio suggests in his blog, but it’s getting more and more attention because:
- Technology is more mature, hence it’s easier to implement claim-based identity
- Enterprises are failing to control the amount of different identity repositories, leading to higher provisioning/deprovisioning costs, security problems, etc.
- End users want simpler user experiences and less passwords
- The cloud makes all these even more challenging
We started with this project a couple of weeks ago planning the content. The approach we decided to use was heavily driven by scenarios (aka zero bulls**t). We used the visual metaphor of a tube map with scenarios being the stations separated in two main lines:
- The blue one, the Enterprise track approaches the federated identity problem from the point of view of a company with many applications that wants to implement SSO and Federation. The main stations are SSO (within the enterprise), Federation (with partners), SOAP Web Services (and flow of identity across services), SSO with a third party cloud app and some variations like: what if the company decides to host an application on the cloud (namely Windows Azure); or what if the company needs to integrate with an application that talks SAML protocol (i.e. Salesforce, Google Apps)
- The yellow one, ISV track on the other hand tackle the problem from the perspective of an ISV that wants to offer an application as a service (think about Salesforce or Dynamics CRM Online as the canonical examples). In this track we start by explaining how to implement federated identity for a cloud application. Then we show how to automate federation to on board new customers. We also show things like exposing a REST API and how that plays with claims; how to integrate with LiveID (or OpenID) for small customers that don’t have an Identity Provider in place; and we end up explaining how to do auditing/billing with claims.
I’m very proud and excited about being part of such a great team including: Dominick Baier, Vittorio Bertocci, Keith Brown, David Hill and Eugenio Pace. I’m sure that something great will come up from this team, the board of reviewers and the community that will help to prioritize and keep the focus!
OpenID – WS-Fed Protocol Transition STS
July 14th, 2009
I will go straight to the point in this post. This is a possible architecture if you want to allow OpenID authentication in a claims-aware WS-Federation-compatible web application. In this architecture there are three actors:
- the web application (aka the relying party)
- the OpenID provider (myopenid, Google, Yahoo, etc.)
- the “protocol broker” STS that “translates” WS-Fed to OpenID and viceversa
These are the interactions that happen at login time:
NOTE: the diagram shows the interactions in a conceptual fashion. In reality all these arrows are HTTP requests/responses that are originated in the user browser.
- The user browse the app
- The Geneva Framework WSFederationAuthenticationModule detects the user is anonoymous and is trying to access a protected page. So it will create a WS-Fed SignIn Request against an STS
- This STS, built with Geneva Framework, will provide a login page that will do the handshake against an OpenID Provider. To do this we use DotNetOpenAuth. In fact this STS will be an OpenID Relying Party.
- The user will provide its OpenID identifier and the STS will issue an authentication request against the OpenId Provider
- The OpenID Provider will ask for a password and will return an authentication response to the STS
- The STS will grab the claims issued by the OpenID provider and will put them in the ClaimsIdentity, generating a SAML token with OpenID claims.
- The STS will return a WS-Fed SignIn Response with the SAML token. The WSFederationAuthenticationModule will grab the token, validates it and generate a principal.
- The user can now access a restricted page because it’s authenticated. But also we have profile information (if the user filled his profile in the OpenID provider)
So essentially what we’ve done is a Protocol Transition STS (don’t know if such term exists), that will transform WS-Fed to OpenID and viceversa.
| Aside This is powerful because we can now plug this STS with a Geneva Server and keep all of our applications WS-Fed compatible. Geneva Server can be used as an R-STS that all the applications will trust. This Geneva Server might be configured with different Identity Providers, one of them could be the “OpenID STS” that we’ve just described. |
Here are some screenshots working with myopenid.com and Google OpenId provider.
Notice that we are getting back some profile information (like email, full name, etc.). We are translating that profile info into SAML attributes that will be issued by the OpenID STS. The following code shows what we are doing on the OpenID STS login page. We translate the OpenID claims and store them in session to get them later from the STS.
protected void OpenID_OnLoggedIn(object sender, OpenIdEventArgs e) { Dictionary<string, string> claims = GetClaims(e.Response); HttpContext.Current.Session.Add(”OpenIDClaims”, claims); } private Dictionary<string, string> GetClaims(IAuthenticationResponse response) { Dictionary<string, string> claims = new Dictionary<string, string>(); claims.Add(System.IdentityModel.Claims.ClaimTypes.Name, response.FriendlyIdentifierForDisplay); var claimsResponse = response.GetExtension<ClaimsResponse>(); if (claimsResponse == null) return claims; if (claimsResponse.BirthDate.HasValue) claims.Add(System.IdentityModel.Claims.ClaimTypes.DateOfBirth, claimsResponse.BirthDate.Value.ToString(”o”)); if (claimsResponse.Country != null) claims.Add(System.IdentityModel.Claims.ClaimTypes.Country, claimsResponse.Country); if (claimsResponse.Culture != null) claims.Add(”http://openid-custom/identity/claims/culture”, claimsResponse.Culture.ToString()); if (claimsResponse.Email != null) claims.Add(System.IdentityModel.Claims.ClaimTypes.Email, claimsResponse.Email); if (claimsResponse.FullName != null) claims.Add(”http://openid-custom/identity/claims/fullname”, claimsResponse.FullName); if (claimsResponse.Gender.HasValue) claims.Add(System.IdentityModel.Claims.ClaimTypes.Gender, claimsResponse.Gender.Value == Gender.Female ? “Female” : “Male”); if (claimsResponse.Language != null) claims.Add(”http://openid-custom/identity/claims/language”, claimsResponse.Language); if (claimsResponse.Nickname != null) claims.Add(”http://openid-custom/identity/claims/nickname”, claimsResponse.Nickname); if (claimsResponse.PostalCode != null) claims.Add(System.IdentityModel.Claims.ClaimTypes.PostalCode, claimsResponse.PostalCode); if (claimsResponse.PostalCode != null) claims.Add(System.IdentityModel.Claims.ClaimTypes.Locality, claimsResponse.TimeZone); return claims; }
This is the code in the STS:
protected override IClaimsIdentity GetOutputClaimsIdentity( IClaimsPrincipal principal, RequestSecurityToken request, Scope scope ) { ClaimsIdentity outputIdentity = new ClaimsIdentity(); if ( null == principal ) { throw new InvalidRequestException( “The caller’s principal is null.” ); } var openIdClaims = HttpContext.Current.Session["OpenIDClaims"] as Dictionary<string, string>; foreach (var openIdClaim in openIdClaims) { outputIdentity.Claims.Add(new Claim(openIdClaim.Key, openIdClaim.Value)); } return outputIdentity; }
Logging in with Google OpenID provider
Since Google does not provide profile info, we get a hash as a login name. We could use OAuth and fetch profile attributes from Google (like email, name, etc.) in the STS and fill more claims. But to do that you need to host the site publicly and register it at Google, it does not work in localhost.
Summary
In this post we showed how you can use Geneva Framework on your claims-aware applications and authenticate against OpenID which is a different protocol. What I really like about Geneva Framework is that it allows you to transition any authentication scheme with WS-Fed because it plays well with the ASP.NET pipeline. So it’s basically an adapter between *any* existing authentication investment and WS-Federation and SAML token. This reminds me of a blog post Vittorio wrote some time ago Enhance your ASP.NET Membership-based website by adding Identity Provider capabilities (hint: read the last paragraph of his post).
Here is the code for the STS and a sample relying party created with the Claims-Aware Website template from Geneva Framework.
Drink the red token – debug your claims-aware apps
June 30th, 2009
Another identity development widget brought to you by Vittorio’s team. This time it’s a very tiny control called
SecurityTokenVisualizer that helps debugging claims-aware web applications. But the thing I like of this control is that it can also be used as a teaching tool, very useful for demos by the way. Last week we “dogfooded” it during the Iteration Review of an identity-related project and the audience made the click when we expanded the red small pentagon that shows the behind the scenes.
Simply drag and drop the control on any page and it will render a collapsible table with
- Issued Identity claims
- Delegated Identity claims
- The raw SAML token (in a textarea)
- The SAML token properties (validfrom, validto, audience, signing certificate, etc.)
- The certificate used for token decryption
As usual, read the details on Vittorio’s blog.
ASP.NET control for claims-aware apps
June 19th, 2009
- Claim Value: set the value of a property with the value of a specified claim type
<cc1:ClaimsDrivenModifierControl ID=”cdmFirstName” runat=”server” ControlToDrive=”txtFirstName” PropertyToModify=”Text”> <Expression Type=”ClaimValue”> <ClaimType>http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname</ClaimType> </Expression> </cc1:ClaimsDrivenModifierControl>
- Mapping: it will set the value of a property based on a set of conditions with an output <cc1:ClaimsDrivenModifierControl ID=”cdmMemebershipLevel” runat=”server” ControlToDrive=”imgMembershipLevel” PropertyToModify=”ImageUrl”> <Expression Type=”Mapping”> <cc1:Mapping ClaimType=”http://cloudbuddies.com/2009/06/FrequentFlierLevel” ClaimValue=”silver” Result=”~/img/silver.png” /> <cc1:Mapping ClaimType=”http://cloudbuddies.com/2009/06/FrequentFlierLevel” ClaimValue=”gold” Result=”~/img/gold.png” /> </Expression> </cc1:ClaimsDrivenModifierControl>
And of course you can use the designer to set this all up. I’m not a huge fan of server controls and designers but I know that there are people out there that loves them, so this is something that will bring you closer to developing claims-aware applications.
Download the control from http://code.msdn.microsoft.com/ClaimsDrivenControl/Release/ProjectReleases.aspx?ReleaseId=2861
The following table shows an analogy of identity concepts between a single application and a federated application.
The single app has its own identity silo and the federated app relies on an STS (like Geneva Server). I find this analogy useful to explain how things differ from the non-federated non-claim-based world.
Scenario: Token Exchange when you can’t change the client
May 23rd, 2009
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); return claims;}
var token = GetToken("matias", "password", new Uri("https://genevaserverurl/Trust/13/UsernameMixed"), CertificateUtility.GetCertificate(StoreName.TrustedPeople, StoreLocation.LocalMachine, "CN=Geneva Signing Certificate…"), new Uri("http://hostname/someservice"), CertificateUtility.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=hostname"))
How To: decrypt a GenericXmlSecurityToken with Geneva Beta 2
May 20th, 2009
This post had a lot of visits so I have updated it to work with Geneva Beta 2
1: public static ClaimsIdentityCollection ToClaimsIdentityCollection(this GenericXmlSecurityToken originalToken, TrustVersion trustVersion, X509Certificate2 signature, X509Certificate2 encryption)
2: {
3: var tokenReader = new StringReader(originalToken.TokenXml.OuterXml);
4: var reader = XmlReader.Create(tokenReader);
5:
6: var privateKeyToken = new X509SecurityToken(encryption);
7: var issuerKeyToken = new X509SecurityToken(signature);
8: var tokens = new List<SecurityToken>();
9: tokens.Add(privateKeyToken);
10: tokens.Add(issuerKeyToken);
11: SecurityTokenResolver outOfBandTokenResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(new ReadOnlyCollection<SecurityToken>(tokens), false);
12:
13: var handlers = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
14: var samlHandler = handlers[typeof(SamlSecurityToken)] as Saml11SecurityTokenHandler;
15: samlHandler.ContainingCollection[typeof(EncryptedSecurityToken)].Configuration.ServiceTokenResolver = outOfBandTokenResolver;
16: var issuerRegistry = new ConfigurationBasedIssuerNameRegistry();
17: issuerRegistry.AddTrustedIssuer(signature.Thumbprint, signature.Subject);
18: samlHandler.Configuration.IssuerNameRegistry = issuerRegistry;
19:
20: var serializer = new SecurityTokenSerializerAdapter(handlers,
21: SecurityVersion.WSSecurity11,
22: trustVersion,
23: trustVersion == TrustVersion.WSTrust13 ? SecureConversationVersion.WSSecureConversation13 : SecureConversationVersion.WSSecureConversationFeb2005,
24: false,
25: null,
26: null,
27: null);
28:
29: var samlSecurityToken = serializer.ReadToken(reader, outOfBandTokenResolver);
30: reader.Close();
31:
32: var claims = handlers.ValidateToken(samlSecurityToken);
33:
34: return claims;
35: }
Geneva Framework: Identity Development Training Kit
May 15th, 2009
During the last couple of months I’ve been helping the Microsoft DPE team (namely Vittorio and Donovan) building the Identity Development Training Kit. It’s been great to work with such knowledgeable guys like them and with one of the best frameworks I’ve ever developed with: Microsoft Geneva Framework. ![]()
The training kit covers a lot of interesting scenarios related to claim-based identity. Here is the shortcut list (if you want a full explanation of each one, read Vittorio’s post)
- Lab: Web Sites and Identity:
- Exercise 1: Enabling claims based access for an ASP.NET Web Application by generating a local STS
- Exercise 2: Customizing the Credentials Accepted by a Local STS
- Exercise 3: Accepting Tokens from a Geneva Server STS
- Exercise 4: Accepting Tokens from Live ID
- Exercise 5: Accepting Tokens from .NET Access Control Service
- Exercise 6: Invoking a WCF Service on the Backend via Delegated Access
- Lab: Enhancing an ASP.NET Membership Provider Website with Identity Provider Capabilities
- Lab: Web Services and Identity
- Exercise 1: Using Geneva Framework for Handling Authentication and Authorization in a WCF Service
- Exercise 2: Accepting Tokens from a Geneva Server STS
- Exercise 3: Accepting Tokens from .NET Access Control Service
- Exercise 4: Invoking a WCF Service on the Backend via Delegated Access
We made sure that all of the exercises followed the best practices of developing with Geneva Framework. Building this training kit was a big effort and I would like to mention the great team that helped creating this: Ariel “lutz” Neisen, Jonathan “passive” Cisneros, Ezequiel “checklist” Sculli and Sebastian “pattern” Iacomuzzi
I invite you to take a look at the training kit and open your mind with the new possibilities the Geneva Framework brings into the table.
UPDATE: the code has been updated to work with WIF RTM. Thanks Nico!
Providing the federation metadata for your STS will be very useful when a relying party want to establish a trust relationship with your STS. For instance, the Geneva Framework provides a FedUtil.exe tool that allows you to point to this metadata file and configure the relying party changing the microsoft.identityModel section (read more about the metadata format here: http://www.oasis-open.org/committees/download.php/30005/ws-federation-1.2-spec-ed-09.doc)
The metadata is signed with the STS private key, which make sense because you don’t want someone else publishing a metadata file and claiming that it’s your STS metadata. That means that you will need some code in order to generate that signature based on the metadata content.
Well I have good news for you. Microsoft Geneva Framework provides a couple of useful classes (like MetadataSerializer) to generate the metadata.
Disclaimer: this code generates a simple version of federation metadata for an IP passive STS (it does not include WS-Trust endpoints for active profile for instance).
var stsUri = new Uri(“https://login.mysts.com/FederationPassive”); string destFolder = @”d:\Temp\”; string signingCertificateSubjectName = “CN=localhost“; var claimsOffered = new DisplayClaim[] { CreateDisplayClaim(”http://schemas.xmlsoap.org/claims/Group”, false, “Group”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/Issuer”, false, “Issuer”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/Email”, false, “Email”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/FirstName”, false, “FirstName”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/LastName”, false, “LastName”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/CostCenter”, false, “CostCenter”, string.Empty), CreateDisplayClaim(“http://schemas.xmlsoap.org/claim/Phone”, false, “Phone”, string.Empty) }; CreatePassiveStsMetadata(stsUri, signingCertificateSubjectName, claimsOffered, destFolder);
The code above shows the usage for a sample STS. Download the code from here
