Handmade Claims-based Authentication for Old-fashioned ASP.NET Sites

Comments 1

Share to social media

According to Microsoft, ASP.NET Identity is going to be the API for present and future membership systems built around ASP.NET web sites. ASP.NET Identity works with OWIN-the emerging standard for setting the interface for web servers and web applications to communicate-and claims-based identity. If you go with ASP.NET Identity then most of what you need to do in today’s applications is half, or entirely, baked into the framework. Nothing needs to be said; the level of engineering in the ASP.NET Identity framework is perhaps a bit suspect; but as far as functionality is concerned, the framework is just right.

Why is the notion of a ‘claim’ in claims-based identity so attractive, or just necessary, today? A claim is a statement about an identity by a ‘subject’, most likely a user attempting to log in into our system. A claim is a name-value pair and carries a value about a particular aspect of that user. Altogether, claims fully represent the user and how it should be presented within the application. Every logged user must have a set of claims; at the very minimum a logged user has a name: the ‘user name’ is the primary and essential claim, but a claim can comprise a range of information about the user, all packaged up in a secure token. Another way to look at claims is as the collection of attributes of an ASP.NET identity.

In classic ASP.NET, whether Web Forms or MVC, the set of claims associated with each identity is limited to the user name and perhaps role. Modern applications, however, require additional information such as picture or avatar, email, and even more application-specific information such as some account balance, inbox messages, timezone.

This article presents a way to simulate claims-based identities in the context of plain old ASP.NET sites. The solution presented is general enough to work with both ASP.NET Web Forms and ASP.NET MVC. You may not need it if you have plans to use ASP.NET Identity.

The Authentication Token

In order to authenticate a user, any browser must present a valid token at the application’s gate. The token takes the form of a cookie or perhaps an authorization header such as in Basic, NTLM or Kerberos authentication. The web server middleware intercept the request and validates the token, thereby performing user authentication. If no valid token is presented, then the middleware commonly redirects to a login page. After any successful authentication, the request carries a valid token full of claims describing the identity of the freshly-logged user.

Claims about a logged user are stored in the token, most likely in the authentication cookie. Who’s responsible for providing all the claims about a given user? And who’s responsible for storing them into the token and for reading them back? This is just what the authentication framework is expected to do. This is what ASP.NET Identity does for you by considering claims as first-class citizens and giving developers an easy API to query claims from identity-providers. These Identity-providers are usually OAuth-based external providers such as Facebook and Twitter.

What if the identity provider, instead, is a plain membership system that just accepts and process user name and password? In establishing a classic ASP.NET membership, the user logs in and passes in user name and password. If credentials can be successfully validated, an authentication cookie is created that contains all available claims. The same system reads claims back the next time that the cookie is presented for authenticating the user. At its core, the biggest difference between ASP.NET Identity claims-based identities and ASP.NET membership is that core ASP.NET membership only treats the user name as a claim. Any other possible claim available about the logged user is ignored and not persisted in the token.

Retrieving Extra Information Logged Users

Let’s say you want your site to present a user interface like in the figure where user name, role information and picture are displayed.

2173-60f2cdba-dea5-4b22-ab98-cb06f8698ca

Here is some Razor code that may be used to generate the view.

 The key parts in this code are the IF statement that controls what happens when the user is authenticated and the user information that is made accessible via the local currentUser variable. As you can see, the currentUser variable exposes public properties such as DisplayName, PictureUrl, and Role. In addition, it incorporates the ASP.NET Identity object through which the actual name that was used to log in is accessible. In the view, the currentUser variable is initialized as below.

The AppUser method invoked on the HTTP context current instance is an extension method. All it does is retrieving all available claims about the user identity. In this case, PictureUrl, Role and DisplayName are additional claims for the logged user.

There are, essentially, two ways to retrieve additional data for the logged user. Every time the user logs in you can use the name claim-one of the fundamental claims of an identity-as a key to query for additional data in some way, or you can manage to retrieve and save all the claims you’re interested in at the point when the user logs in. In the first case, the authentication cookie only contains the user name; You will then require a database query-or some sort of query- to grab the requested information every time. You typically have some Users database table and grab from there all information you may need about the user profile. As an alternative, you can query this information only once, when you first validate the user and create the authentication cookie. You would then store it in the cookie itself so that it will be promptly available for future reading.

The former approach doesn’t require any special code to be written: it’s just a plain database table query. You then have to solve the problem of making the user information available to all potentially-interested views. You might want to consume this information from the main layout view and display it in all Razor views. A possible approach is to have a user-related property in the base class from which all view model classes inherit. Or, if you don’t use view model classes, you can just insert the user-related property in the Razor’s view bag.

If you choose to serialize claims within the authentication cookie you need some infrastructure code so that user claims can be extracted from the same User property you find in the HTTP context. The User property of HttpContext is defined as below:

In the .NET Framework, the IPrincipal interface represents the minimal public interface of the security context of the user on behalf of which the code is running. The principal includes the identity of the user-usually abstracted to an IIdentity object-and any roles. In the .NET Framework 4.5, you have the ClaimsPrincipal class that works as a predefined container of claims identities. That is the type of principal that ASP.NET Identity relies on.

In order to handle custom claims in the context of an old-fashioned ASP.NET application, we need to build our own principal object that entirely mimics the public interface we expect to find in the object that represents the logged user.

Creating a Custom Principal

A custom principal is a class that implements the IPrincipal interface. The class extends the IPrincipal interface by adding custom properties and methods. Here’s an example.

In the example, the class exposes two constructors-one building an instance of the custom principal from a basic principal provided and another one building an instance of the principal from some user-related object that the application knows about. Most typically the ApplicationSpecificUser class is the application-specific class used to represent a user account. Expect it to be a class with a direct correspondence to some database table where user credentials and profile information is held.

To get an instance of this class, you invoke the User property on the HTTP context current instance. The User property on HttpContext, however, is of type IPrincipal. This means that in order to use the actual properties in the custom principal interface you need to cast. An extension method can do the job in a more elegant way than the plain cast operator of the language of choice.

In light of this code, we can now initialize a variable in any Razor view to be the container of user information.

The next thing to plan for is how to create an authentication cookie that contains additional information such as picture URL and role. Some code is required that is used in lieu of the methods of the plain old FormsAuthentication class.

Creating a Customized Authentication Cookie

The code below shows a method that you can use to create an authentication cookie with custom ad hoc content. It should be noted that, since the beginning, ASP.NET has provided the infrastructure for developers to stuff custom content in the default authentication cookie. Let’s see what’s required.

The cookie is created through the services of ASP.NET and is simply populated with an encrypted ticket that contains user information. Custom content goes in the UserData property of the FormsAuthenticationTicket class and is obtained through a custom serialization process operated on the application-specific user object. The serializer can be as simple as below:

If your application provides a specific screen for user to edit their profile, you might want to also provide a method to update an existing cookie, for example when the user changes the picture or the display name. When this happens, either you log the user out and require a new login or just silently update the cookie with new information. The final step required to fully-configure a custom principal is to parse the cookie right after the authentication has taken place. The entry point in this kind of code is the PostAuthenticateRequest in global.asax.

The procedure to follow is fairly simple. You extract the ticket information from the authentication cookie and parse out the custom content. It was previously serialized as a comma-separated string so deserializing it is now a matter of splitting the string and loading individual values into proper fields of a newly created instance of the ApplicationSpecificUser object. When all this is done the final step is creating the principal object and attaching it to the User property of the HttpContext current instance.

Summary

In ASP.NET, a custom principal has been just an option available to developers for years. Many used it, but it’s never been a decisive factor in the building of ASP.NET applications. For many years, just recognizing the user by name was more than enough. Today, instead, we tend to be more oriented to claims and want to display more information about a user. This fact raises the problem of finding claims. If you use external providers for authentication then it’s those providers that should return claims. If you use an internal username/password login provider then you might want to look back at custom principals to set up a claims-based authentication even on top of old-fashioned ASP.NET applications.