Custom Authentication with MVC 3.0
During a friendly code review discussion a week or so ago I realized I’d forgotten my favorite lore of custom authentication/authorization functionality in lieu of ASP.NET P&M. Though I definitely prefer P&M to rolling my own from scratch to the extent that I’ve gone as far as to use it as a pass-through there are some times when P&M is too much – or too little – so custom schemes must be developed. The thing that surprises me each time I observe a custom scheme is the lack of usage of the IPrincipal/IIdentity interfaces. With MVC 3.0 as my major weapon of choice in web development and my recent adoption of P&M, it became obvious that an opportunity had popped up for mixing my favorite old-school .NET trick into my favorite new-school paradigm.
Here’s the thing. If you don’t use the IIdentity and IPrincipal interfaces when you create a custom authentication/authorization solution you’ve completely missed the boat and I guarantee you you will write 100 times more code, that will be more brittle, than you would have ever written had you just used them in the first place.
A quick look into what these two interfaces offer is in order. The graphic above sums it up visually. You’ll see that the IIdentity interface is typically considered a requirement for the IPrincipal to exist; obviously this model fits in rather well with MVC’s favoritism to IoC/DI practices. The principal wraps around the identity, supplying access to a user’s roles. So long as your classes implement the requirements, they can be bound to the HTTP Context, the current thread, basically attached the process so that all the niceties like use of the [Authorize] attribute and role-based location security via the web.config file are possible without tons of rework.
The first class needed in any custom implementation is the identity class. The main purpose of this implementation is to represent the user’s name, really, as well as how the user was authenticated and if they areauthenticated. Should the IsAuthenticatedproperty be set to false at run-time, code later on assumes the user is an anonymous user.
Next will be the CustomPrincipalclass. Since this class implements IPrincipal,it can be used to bind itself directly to a thread (or an HTTP Context). Since it can be used in that manner, all the functionality and support offered via web.config-based authorization, use of the [Authorization] attribute, all of that – it will be maintained and you won’t have to write it (or support it, or debug it, and so on). Note how the constructor’s argument is highlighted to illustrate the connection between the two classes.
To wrap these classes up via a native event handler or inherited method we’ll create a BaseController. It could – and probably should – be done via an injected service, obviously, but I’m honoring the KISS principle [you say principle, I say principal] for the purposes of this walk-thru. The base controller is shown below and its main purpose in existing – to authorize the active user account prior to allowing execution of the controller action.
Caveat: This next part isn’t a recommendation, it’s only used for the purposes of keeping this explanation simple. Forgive me.
Now that we’ve satisfied the authorization part we’ve got to authenticate the user and store their login information somewhere for the life of their… yeah, you guess it, for the life of their session.We’re going to use Sessionhere, okay, but just for the demo. It isn’t a recommendation. Please turn offthe tape recorder.
The SimpleSessionPersister class below persists the user’s name into a session variable [ducks under flying tomato]. The login controller action below the utility class just validates that the user provided somethingfor their username and password and, if so, the user’s username is persisted to session for use – by the BaseController,specifically – later during the HTTP request.
You can relax now, that part’s over.
Now that the user has been authenticated we can assume that each controller we create (provided it inherit from our custom base) will authorize each individual request per the best-practice role-base security means. Take the following controller action below, which requires the user be authenticated via the [Authorize] attribute and the web.config, also below, which indicates the login page URL as our Login controller action.
Should a user try to hit the non-anonymous page they’d be redirected to the login page (see the URL highlighted below).
To review the process we went through to accomplish our very own custom-made end-to-end security paradigm that still allows usage of the .NET built-in role-based security resources, here’s what we had to do:
- Implement the IIdentity interface in a custom class
- Inherit from the IPrincipal interface in a custom class
- Create a way to authenticate the user
- Create a way to persist the authentication information [a.k.a. username in this example]
- Override the OnAuthorizationmethod in a base controller and then use that base controller in future controllers
Happy Coding! If you’d like to take a peek at the code for this demo and debug it for better observation as to how it all works together you can download the code from my DropBox account.