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
Identity thoughts #2: Level 2 Authorization
June 17th, 2009
In my last post I talked about an identity roadmap and how we are helping companies to achieve Level 1: Externalizing Authentication. In this first level, we only care about checking the credentials of a user in a Security Token Service and issue a token with a couple of claims. That token will be enough to prove access to the application.
Reaching the Level 1 will make a great difference to the IT department of a given company. By having a central login place they will be able to answer to questions like “when someone logged in to a certain application?”, “which applications someone used in this timeframe?”, etc. In terms of governance, having a single way to implement login will allow the architecture department (if any) to decrease security threats because there is a single well thought piece of infrastructure to perform user authentication across all apps. As a side effect it will also reduce costs of development and maintenance.
Level 2 talks about the authorization process. The authorization decision happens near the application or the service because it knows about the resource (each application has a different domain model).
Geneva Server – Level 1 Authentication
- SSO & Federation – unified login experience and federation with partners
- Centralized Claim Mapping management
- Externalize authentication
- Near the identity provider
- Questions it will answer
- When a subject logged in to an app?
- What claims the subject presented to the app?
Policy Server - Level 2 Authorization
- Policy Enforcement
- Centralized Policy Rules management
- Externalize authorization
- Near the application
- Questions it will answer
- What permissions did the subject requested?
- What permissions where denied?
The following figure shows a very high level architecture of the components and its interactions
Resources
[XACML] http://www.oasis-open.org/committees/xacml/
[Geneva Server] http://connect.microsoft.com/content/content.aspx?ContentID=10106&SiteID=642
[SAML] http://www.oasis-open.org/specs/#samlv2.0
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.
Microsoft Architecture Day: Roadmap to Identity
May 27th, 2009
Today Microsoft hosted the Architecture Day. This is an interesting event where architects from different companies come together to talk about different topics (similar to the Regional Architect Forum). In this opportunity I proposed to do a presentation about Identity related to some work we did with a customer on Microsoft “Geneva”. So together with Sebastian Renzi (who was the PM in that project) we did a one hour presentation covering the basis of claim based identity and a demo of the work we did in this customer.
The presentation started by telling a story about a typical company that created its first application. This app used Windows Authentication so life was easy :). However business grew over time and they ended up with multiple applications, each one with its own identity repository, different authentication methods, support users accessing from intranet, internet, extranet, cloud, and so on and so forth. I grab this diagram from Stuart Kwan presentation from the Genevea Product Team (so thanks Stuart for the cool representation).
Then we went through the problems from different perspectives: the department who is in charge of security, the end user and the architect. (I wish I had the time to translate it to English but you can imagine…)
If you think about these problems, many of them can be solved if you follow this principle:
Externalize from the application logic the following responsibilities:
- the process of authentication
- the retrieval of attributes that will be used later for authorization
This is exactly what claim based identity is about. We setup an analogy between how an event registration works under the same principles of credentials, STS, claims and resources (kudos to Eugenio Pace who originally did this analogy in his PDC presentation).
Now, if I have to start a company I certainly would create my STS and applications and will use claim based identity. However, in the real world we know that the landscape is much more complex and there are existing applications with identity silos running everyday. Any disruptive change will be a huge mess for everyone. So this is the roadmap we established together with this customer (which is one of the biggest insurance company here in Argentina):
We decided to tackle authentication first and try to remove the identity silos in an organic fashion. Since they had many applications (some of them using SQL Server, some others AD, some others a custom mechanism) we wanted to rely on users to do the migration as opposed to a huge migration from IT. This also allowed IT to debug the user database. The following figure shows how the consolidation of identity would happen through time. It will work as fast as users will do its first login and create the mapping with the new repository. This works well when you have lots of apps using a database as the user repository.
Note: this is not based on real data, it tries to give you an idea of how it works over time (credits to Johnny Halife).
We did a demo on how this works and people were interested in this approach because it is not disruptive.
I posted the presentation in Spanish and you can download it from here
Some useful resources
Identity Development Training Kit: http://snipurl.com/identitytk
“Geneva” Download: http://snipurl.com/genevadownload
Channel9 Identity: http://channel9.msdn.com/identity/
Blog Vittorio Bertocci: http://snipurl.com/vibro
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
Multi tenant federation with Geneva Framework and Microsoft .NET Services Access Control
April 23rd, 2009
A typical scenario for an ISV that wants to create the "next application in the cloud" will be how to support identity federation with their customers (tenants). A common requirement I’ve heard is:
"I want to enable single sign on and allow enterprises that have their own STS to integrate with us. For companies that don’t have any identity infrastructure in place we want to allow them to login with an ubiquous credential like Windows LiveID. How do we do that without spending three months with a security guru?"
A possible answer is use Microsoft .NET Services Access Control. They enable that scenario in a very straightforward fashion. The following diagram shows a possible architecture that might fulfill the customer requirements. In this picture Southworks is an enterprise that has its own STS and Contoso doesn’t, hence they use Windows LiveID for their users. The good thing about this is that in the middle we have ACS acting as the "normalizer". It will receive tokens from LiveID and Southworks IP STS and will transform them to something Fabrikam knows (Roles, Actions, etc.).
If you are like me, you might be wondering how this all works. Here are the gory details of all the HTTP interactions of a WS-Federation passive profile "dance":
- A user opening a browser
- If he is at Contoso he browses to www.fabrikam-cloudapp.com/contoso (or contoso.fabrikam-cloudapp.com)
- If he is at Southworks he browses to www.fabrikam-cloudapp.com/southworks (or southworks.fabrikam-cloudapp.com)
- At the web site there is an Http Module or ActionFilter in ASP.NET MVC that will read the tenant alias from the route.
- The module will construct the SignInRequestMessage federation message and will redirect to ACS (https://{solution-name}.accesscontrol.windows.net/passivests/{federation-endpoint}).
- If it’s Southworks tenant we have to use the https://{solution-name}.accesscontrol.windows.net/passivests/Federation.aspx endpoint of ACS.
- If it’s Contoso we have to use https://{solution-name}.accesscontrol.windows.net/passivests/LiveFederation.aspx in the endpoint of ACS.
Note: these urls are not in any ACS documentation for now - The homeRealm (whr) parameter will tell ACS which IP STS to use (Contoso = login.live.com, Southworks = login.southworks.net which is a url Southworks provided at provisioning probably).
- Finally, the realm parameter is fabrikam-cloudapp application. This will have to match with the scope you create on ACS (more on that below).
Note: Look at the code at the end of the post to see how homeRealm and realm is used. - ACS will redirect the user to either LiveID or the company IP STS depending on the whr parameter
- The user will login with LiveID cred or with other mechanisms if it’s a Geneva Server (user/pwd, kerberos, certs, cardspace, etc.)
- A token will be issued and will be POSTed to ACS federation endpoint
- [Contoso] In the case of LiveID you don’t have to do anything because ACS setup all the federation for us. LiveID will issue a token with one claim: the WLID email.
- [Southworks] In the case of the company IP STS you will have to configure ACS as a relying party and supply its certificate to encrypt the token in your IP STS. In ACS you will have to create an Issuer and upload the company IP STS certificate. The company STS will issue the name claim and maybe groups or other claims.
- ACS will read the token and apply the claim transformation (more on that below)
- ACS will POST the token to www.fabrikam-cloudapp.com
- Geneva Fx on the website will read the token, generate the ClaimsPrincipal and store it in a cookie for later usage.
- Fabrikam can now authorize access to certain pages by reading the principal! either with IsInRole or for granular checks you could use Actions or a ClaimsAuthorizationManager
This is how ACS is configured for :
A better way to look at the table above is the following diagram:
Here we are using the Geneva Fx manually inside an ASP.NET MVC action filter. We use it manually because the multi tenancy nature that we want to implement does not allow to use the fixed values from the wsFederation configuration section.
1: namespace FabrikamCloudApp.Web.Identity
2: {
3: using System;
4: using System.Globalization;
5: …
6:
7: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
8: public sealed class WSFederationAuthenticationFilterAttribute : ActionFilterAttribute, IExceptionFilter
9: {
10: public override void OnActionExecuting(ActionExecutingContext filterContext)
11: {
12: var principal = Thread.CurrentPrincipal;
13:
14: if (!principal.Identity.IsAuthenticated)
15: {
16: string realm;
17: string homeRealm;
18: string acsEndpoint;
19: SignInRequestMessage message = null;
20:
21: // retrieve tenant from url (http://…/{tenant})
22: var tenant = (string)filterContext.RouteData.Values["tenant"];
23:
24: // lookup in some repository if the tenant setup a federation with its own sts
25: if (IsLiveIDFederation(tenant))
26: {
27: acsEndpoint = "LiveFederation.aspx";
28: homeRealm = "http://login.live.com";
29: }
30: else
31: {
32: acsEndpoint = "Federation.aspx";
33: homeRealm = GetIdentityProviderUrl(tenant);
34: }
35:
36: realm = "http://www.fabrikam-cloudapp.com/";
37: string issuer = "https://{solution-name}.accesscontrol.windows.net/passivests/{federation-endpoint}"
38: .Replace("{solution-name}", Configuration.FabrikamSolutionName)
39: .Replace("{federation-endpoint}", endpoint);
40:
41: message = new SignInRequestMessage(new Uri(issuer), realm);
42: message.Parameters.Add("whr", homeRealm);
43:
44: // redirect to ACS
45: filterContext.Result = new RedirectResult(message.WriteQueryString());
46: }
47: else
48: {
49: // we are back on our site, and we’ve got our token transformed to ClaimsPrincipal
50: var claimsIdentity = principal.Identity as IClaimsIdentity;
51:
52: var tenantClaim = claimsIdentity.Claims.FirstOrDefault(c => c.ClaimType == Configuration.TenantClaimType);
53: if (tenantClaim == null)
54: {
55: throw new HttpException(401, "Access is denied");
56: }
57:
58: var fam = FederatedAuthentication.WSFederationAuthenticationModule;
59: if (fam.CanReadSignInResponse(HttpContext.Current.Request, true))
60: {
61: SecurityToken token = fam.GetSecurityToken(HttpContext.Current.Request);
62: SessionSecurityToken sessionToken = new SessionSecurityToken(principal as IClaimsPrincipal, token);
63: fam.SetPrincipalAndWriteSessionToken(sessionToken, true);
64:
65: filterContext.Result = new RedirectToRouteResult(
66: "DefaultRoute",
67: new RouteValueDictionary(new { tenant = tenantClaim.Value }));
68: }
69: }
70: }
71:
72: }
73: }
And just put this attribute on your entry point Controller:
1: public class HomeController
2: {
3: [WsFederationAuthenticationFilter]
4: public void Index()
5: {
6: }
7: }
I can tell my customer "sure, we can implement this. It’s only 73 lines of code and some configuration here and there"
Happy Federation!
IssueTracker Azure Edition - a Cloud Application
February 13th, 2009
Couple of weeks ago Ryan Dunn announced Azure Issue Tracker. From this post:
"This sample application is a simple issue tracking service and website that pulls together a couple of the Azure services: SQL Data Services and .NET Access Control Service."![]()
I’ve been working with Ryan and other guys at DPE and Southworks to put together this sample before PDC. With all the back and forth (the .NET services were not working as reliable as they work now) we were not able to pull it through at that time. Well, it’s now live and you can download the source code. Some of its features:
- [Identity] .NET Services Access Control as a relying party and claims transformation STS
- [Identity] Federation against LiveID and claim mapping between email -> tasks. I hinted the implementation in these post.
- [Identity] Claims aware application and service layer (by doing identity delegation with ActAs)
- [Data] Storage on SDS using the flexible schema to extend the data model of the issue
- [General] Multi tenancy at all levels (identity, data, programming model)
- [General] Clean separation of concerns using ASP.NET MVC, Geneva Framework, WCF and WF.
This is the standard edition. The enterprise edition is coming with features related to manageability (Management API, Powershell CmdLets, MMC, SCOM, etc.) and identity federations against third party STS. Stay tuned!
