Since the release of .NET 1.0 more than ten years ago, the classes governing identity have remained unchanged. That’s a good thing, because identity and security is at the core of most applications, so you don’t want that to change very often. However, with the release of .NET 4.5, the identity model has changed significantly.
In case you haven’t noticed, there’s a paradigm shift going on in the world of security. For decades, we’ve relied on firewalls to keep unauthorized people off our networks, and within applications we’ve been authorizing users based on their user accounts and the associated groups. Although this has worked very well, and still does for quite a few applications, this model is isn’t very flexible. It mainly works in closed networks and when users and roles are concrete. But networks are no longer isolated. With more and more devices out of our local network, applications being moved to the cloud, and an ever increasing focus on collaboration, the need for an open environment increases. Forget the firewall, users from outside your office walls need to access applications on your network (and vice versa). The question has shifted from “how do we keep people off our network?” to “how do we identify users (from any location) and make sure they can only access what they are authorized for?” The new identity model in .NET 4.5 is here to help you answer the latter question.
Where Did We Come From?
Before we look at what’s new, let’s briefly look at where we were before .NET 4.5. Ever since .NET 1.0, security has been structured around two interfaces: IIdentity and IPrincipal. A class implementing the IIdentity interface represents the identity of the user. The IIdentity interface is shown in Listing 1.
The simplicity of the IIdentity interface is very elegant, but also somewhat limiting. There is no way to convey additional information about the user with this interface. Implementations of the interface need to take care of that themselves, and are therefore not generic. To authorize the user, the identity is not enough. You need a class deriving from IPrincipal, shown in Listing 2.
Basically, the IPrincipal interface wraps the identity and adds a method to check whether the user has a specified role. That method has been the cornerstone of authorization in the last decade. As a consequence, there is an enormous amount of code such as shown in Listing 3. Or you might find code using the PrincipalPermission class or PrincipalPermission attribute, which uses the IsInRole method under the covers, as shown in Listing 4. Although this has worked fine, this sort of code has two major drawbacks: authorization checks are embedded within the code, and authorization checks only work with role information.
The IPrincipal.IsInRole method has been the cornerstone of authorization in the last decade.
The problem of only being able to authorize against roles when you use the PrincipalPermission class or attribute, boils down to the fact that there is no other information about the user. Authorization information that doesn’t fit well within a role, for instance, a transaction limit, is not available using the standard role-based mechanisms. This was partially solved for .NET Framework 3.5 and 4.0 when Windows Identity Foundation (WIF) was introduced. This resulted in the class and interface structure shown in Figure 1. As you can see in Figure 1, WIF adds two interfaces that extend the existing IIdentity and IPrincipal interfaces, shown in Listing 5 and Listing 6.
By far the most important of the new interfaces is the Claims property of the IClaimsIdentity interface. A claim is a piece of information about the user, which is, in essence, a key-value pair. Because of this simple and untyped structure, claims can contain any information, not just username and role information. It is equally valid to put data such as age, social security number, or transaction limit in a claim. Such information can be very valuable when executing business logic and to perform more elaborate authorization checks. Although a claim is primarily a key-value pair, the Claim class contains more information, as you can see in Listing 7.
By far the most important of the new interfaces is the Claims property of the IClaimsIdentity interface.
The ClaimType property in the class from Listing 7 designates the meaning of the claim. Most commonly, this is a unique URI identifying a type; for instance, an email address is designated by http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email. The Value property contains the corresponding value, of course. Note that a claim can exist multiple times, so an identity could have multiple email addresses. The ValueType property indicates the data type, but because you know the type of the claim, you rarely need to know the data type. The Properties dictionary can also contain additional information, such as metadata, about the claim.
From WIF to .NET 4.5
Although WIF was a nice addition to the .NET Framework, you can see in Figure 1 that it is exactly that: an addition. It really stands separate from the classes in the System.Security.Principal namespace. Because of this, it is hard to create components that can be used regardless of how the user is authenticated. In .NET Framework 4.5, this has been fixed. The new identity model is ubiquitous, because WIF has been integrated into the core of the Base Class Library of .NET. All of the classes associated with the new identity model can be found in the mscorlib DLL, which is referenced by any application you create. Figure 2 shows the new structure of the classes and interfaces.
As you can see, the IClaimsIdentity and IClaimsPrincipal interfaces are no longer there. The previously existing identity and principal classes in the System.Security.Principal namespace are all still there, but now these inherit ClaimsIdentity and ClaimsPrincipal from the new System.Security.Claims namespace. This looks a little odd because of the side-step to the System.Security.Claims namespace from a structure that is otherwise completely in the System.Security.Principal namespace, but it also ensures backwards compatibility. The classes that existed since .NET 1.0 are still in their trusted location. The fact that they now have a different inheritance structure is not important to code that doesn’t use claims. That code can run without modification.
However, existing WIF code using the Microsoft.IdentityModel namespace has to be changed, because those classes have basically been moved and the IClaimsIdentity and IClaimsPrincipal interfaces no longer exist. As a consequence, the Claim class has also changed slightly, although it looks almost identical and behaves the same functionally.
Windows Identity Foundation has been integrated into the core of the Base Class Library of .NET.
Getting Started with Claims
The best way to get started with claims-based authentication and authorization is by creating a small Web application in ASP.NET using .NET 4.5. I strongly recommend that you use Visual Studio 2012 for this, along with the Identity and Access Tool for Visual Studio 2012, which you can download from http://bit.ly/IDATVS2012. This add-on to VS2012 contains some very useful tools to make it easy to develop applications using claims.
First, create a Web application in Visual Studio. Whether you use WebForms or MVC doesn’t really matter. Both work fine with claims and the additional tooling. For the purpose of this article, I’m assuming an ASP.NET MVC 4 project with the Internet Application template.
Whether you use WebForms or MVC doesn’t really matter. Both work fine with claims and the additional tooling
Once the site is ready, right-click the project in the Solution Explorer and choose Identity and Access... This yields a dialog box that asks where your users are authenticated. Because each of these methods uses the same protocol, you can easily switch among them. For now, the Local Development STS will do fine. This STS (Security Token Service) provides the security token used to login to your application. Normally, a user gets a token after authentication with the STS, but the Local Development STS issues one with the claims you specify.
When you specify the Local Development STS, you could click OK and you’d be good to go. However, it makes sense to take a look at the Local Development STS tab shown in Figure 3. In this tab, you can add or change the claims sent to the application. You can come back to this tab through the Identity and Access… menu option. When you first run the Identity and Access wizard or when you change the identity provider (more later on that), Visual Studio modifies the web.config. It adds or modifies the configuration sections shown in Listing 8.
Inside <system.identityModel> is the configuration telling the application the requirements of the token. The <audienceUris> element tells the application which URI(s) the received token should be for. If the application receives a token that is not scoped to any of the URIs specified, the token is regarded as invalid and the user will not be able to use the application. The application will throw an exception accordingly.
Inside <system.identityModel> is the configuration telling the application the requirements of the token.
The <trustedIssuers> element tells the application how it can be sure the token comes from an STS it trusts. This is important, because users are no longer logging into the application directly. They login to the STS, which is external to the application, and not under control of the application. Tokens are signed by the issuer to prevent others from spoofing the token, and this allows the application to determine whether the token really came from a trusted STS. Signing is done using public key encryption with a certificate. In the case of the Local Development STS, this is done with a self-signed certificate, which is why the certificate validation mode is set to None. Otherwise, the application checks whether the certificate with which the token is signed is valid. That only works if you used certificates issued by a certificate authority within your Windows domain or by a public certificate authority such as Comodo, GoDaddy, or VeriSign.
The <system.identityModel.services> section deals with where to get a token and how to handle it when you receive it. There’s a lot that you can configure in this section, but there are only a few things you really need if you stick to the defaults. The most important two settings are the issuer and the realm. The issuer is where the application must send the user to get a token, so this is a URL. The realm could be any value, as long as it tells the issuer for which application it is creating the token. This could be important, because not every application needs all claims. In fact, it may be the case that you don’t want to share certain claims with an application. Most issuers will not even issue a token if it doesn’t know the application identified by the realm. As you can see in Listing 8, the audience URI and the realm are the same. That is a convention you see often, but it is not required.
The most important two settings are the issuer and the realm.
With the configuration in place, everything is set. If you now run the application in debug mode, the first thing you’ll notice in the notification area is the Local Development STS starting, as shown in Figure 4.
The Local Development STS doesn’t require a password or anything. In fact, you won’t even notice it’s there, because you are automatically logged into the Web application. If you didn’t change any claim in Figure 3, you are now logged in as Terry. You can see this in the top right hand corner of the Web page, as shown in Figure 5.
You can only see what happened if you look at the HTTP traffic from the first request. Figure 6 shows that traffic in Fiddler (http://fiddler2.com).
In Figure 6, I’ve highlighted two requests that are not to the Web application. You can tell because they go to a different URL (in this case designated by a different port number). These two requests actually show that authentication doesn’t happen inside the Web application. What happens is that the initial request is redirected to the STS, because the user is unauthenticated. The Local Development STS automatically issues a token, as if the user is authenticated. You don’t need to enter any credentials. The experience is similar to having Active Directory Federation Services (ADFS) in your network. ADFS authenticates users based on the account credentials they use when logging into their computer, although ADFS can do much more.
Getting Claims from a Token
When you get a token with claims, it’s handy to see which claims are in it. And not only for you as a developer, but it is actually a nice feature to show the information you’re getting from the STS to the user. This is fairly easy to do, because claims are basically objects containing only data. Using ASP.NET WebForms, you can display these in a table by binding ClaimsIdentity.Claims to a GridView. In ASP.NET MVC, you need to add a controller method that return the same collection of claims to display these in a view, as shown in Listing 9.
Next, you need to create a view. The easiest way to do this in Visual Studio is to add a strongly typed view as shown in Figure 7. Be aware that the Model class drop-down in Figure 7 doesn’t show built-in types. However, to auto generate the view, you want to use this class anyway.
A way around this is to temporarily make a ClaimModel class that inherits Claim. Once Visual Studio has generated the view, just change ClaimModel into Claim and remove the ClaimModel class (or leave it for later use). Now all you have to do is add a link somewhere to access the page displaying the claims. Figure 8 shows you what that looks like.
Notice that you get a nice list indicating the (original) issuer, the type, and the value. To get a feel for what you can do, it’s a good idea to play around with the claims you send with LocalSTS. You could, for instance, add more roles, and use role-based access control as you would in ASP.NET or ASP.NET MVC when you’re working with a Membership provider.
Authorization Using Claims
As just mentioned, you can use role claims to provide role-based access control. Role claims are automatically converted to the roles you get when you use the Principal.IsInRole method. However, using a ClaimsAuthorizationManager is a much better alternative. This enables you to create authorization logic with any claim or combination of claims you like. Also, it separates the authorization logic from the functional code, making security much easier to manage.
Using a ClaimsAuthorizationManager, you separate authorization logic from functional code.
First, you need to create a class that inherits from ClaimsAuthorizationManager (for this you need to reference the System.IdentityModel assembly) in the System.Security.Claims namespace, and override the CheckAccess method. This method returns a Boolean indicating whether the user has access or not. All you have to do is determine the conditions under which a user has access. The CheckAccess method takes a single parameter of type AuthorizationContext, which contains three properties: Action, Principal, and Resource. Both Action and Resource are collections of claims, identifying the resource(s) that access is checked for, and the action(s) taken on the resource(s). For eample, a Read operation on a financial record of a bank account should only be accessible to either bank employees or the holder of the account. Listing 10 shows code that checks exactly that.
Although this example does a straight check against claims, you can also check against a database or another source of information if you want to. This is very useful if your application uses a very granular access control structure. You need to configure the custom ClaimsAuthorizationManager in order to use it, as shown in Listing 11.
There are several ways to invoke the CheckAccess method. For any of them, you need the ClaimsPrincipalPermission class for which you need to reference the System.IdentityModel.Services assembly. The simplest way is using the static CheckAccess method, which takes two strings: the action and the resource, as shown in Listing 12.
You can also use a ClaimsPrincipalPermission attribute on the method being called, as shown in Listing 13. They are functionally equivalent. However, the latter is impractical in real life, because you would want to set the bank account being accessed at run-time.
You might wonder why the examples use a concatenated string as a resource, because the context allows for a collection of resources and actions. The problem is that even if you create a ClaimsPrincipalPermission instance containing multiple permissions to check, these are still sent to the CheckAccess method on the ClaimsAuthorizationManager one by one.
Getting Additional Claims
You can authorize against incoming claims nicely, but what if the claims you receive from an identity provider don’t contain enough information or you get incoming claims that need to be translated into other claims before you can use them? You can solve this problem by creating a custom ClaimsAuthenticationManager, and overriding the Authenticate method.
You can add or modify incoming claims by creating a custom ClaimsAuthenticationManager and overriding the Authenticate method
Listing 11 already showed you how to register the custom class, which is closely related to the ClaimsAuthorizationManager discussed earlier. Listing 14 shows you an example of adding a Role claim, but you can also transform or add claims, for instance, based on data in a database using a unique user identification such as the Name Identifier claim.
Here to Stay
The fact that WIF has been integrated into the .NET Framework tells you that claims-based authentication and authorization is here to stay. And for good reason. As you’ve seen, it is a much more flexible and elegant way to operate than what we had before. Also, it fits very nicely with protocols such as WS-Federation and OAuth, which will also be around for some time. I therefore strongly advise you get to know more of identity in .NET 4.5 than covered here, because there is much more to learn.