This is part four of my articles on writing Custom ASP.NET Authentication. The first article served merely as introduction, but the second delved into writing a Membership Provider and what that entails. The third described building Role-Based Authentication.
Role-Based Authentication is great for many people's goals in handling authentication, but often, particularly as an application grows, you find that you also need to control access to specific resources and not just activities. This is where the need for Claims-based Authentication rears it's head. What's important to understand is that you're often going to end up using a mix of the two, with a leaning toward one or the other.
With .NET 3.0 and Windows Communication Foundation (WCF), Microsoft has finally released a Claims-based authentication mechanism. To be honest, I hate it, but it's there. Like with many Microsoft technologies, I feel it's unnecessarily complex, so while I'll be talking about Microsoft's Claims-based authentication, I'll be talking about the one we're using as well. I'll hold my judgement on code-name Zermatt, but what's currently available in System.IdentityModel is too much for me.
Essentially, you begin by declaring a Claim in your code. The claim is a data-type which represents a resource you want to control access to. The object is simple:
public class Claim { public string ClaimType; public object Resource; public string Right; }
The ClaimType is a URI, which tells you what kind of Claim is being made. The Resource property identifies the resource being requested, and the Right is either custom defined or from the System.IdentityModel.Claims.Rights class to determine what the user wants to do, but we'll get there in a minute. So far, fairly simple.
Next we, move into ClaimSets (this is where I feel things fall apart)
public class ClaimSet : IEnumerable, IEnumerable { public abstract ClaimSet Issuer { get; } public abstract Claim this[int index] { get; } public static ClaimSet System { get; } public static ClaimSet Windows { get; } }
Okay, the System and Windows ClaimSets are provided by Microsoft, Windows being Windows Security, and System being the OS level Claims. Depending on your application, these sets may or may not be important, but it's the Issuer that's most likely to be important. The Issuer can be used to determine the source of a claim, for instance a Web Application versus a Web Service could be different Issuers.
Since the above class is Abstract, you either have to derive a new class from it, or create a default Claim Set to test against. Ultimately, this is going to require a fairly complex custom structure to ensure that your claim sets are accurate to whatever you're trying to accomplish in your application.
In order to test your claims, you call FindClaims against your ClaimSet with a claim you want to test for, and you can use that information to handle authentication. The complexity, and it's painful complexity, is in building the ClaimSet. I'm not wholly convinced it's worthwhile, which is why I went a slightly different route to solve this problem. If you are interested in Microsoft's Claims-based Authentication, MSDN Magazine has an article on it this month.
For our purposes I built Claims-based Authentication into our Role-Based code, buy adding methods to our custom Principal. For us, we need to control access to Courses, Prefixes, and Terms, as well as determine if the user is authorized for a given action for that given Course, Prefix, or Term.
I'm not going to bother with code for this, as it was implemented purely by adding new methods to our implementation of IPrincipal. It works great, allowing us to verify our permissions, as well as a check those permissions to send that permission set to our client. The only downside is that we need to explicitly cast our Principal object in order to access our custom methods. Still, it provides the ability to test claims, without diving into the complexity of the ClaimSet implementation Microsoft is currently providing.
Authentication and Authorization are incredibly important steps in any web application, and Microsoft has done a pretty good job of providing a framework that is very customizable, while being fairly hard to break in an incredibly dangerous manner. However, I had been unable to find very many resources for doing this work, which I suspect many people will find themselves requiring. I hope that this series helps someone, as I know it would likely have helped me.