The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








I heart FeedBurner

Latest Diggs - Programming

Computers Blogs - Blog Top Sites

Site Hits

Total: 3,825,508
since: 19 Jan 2005

ASP.NET MVC Dev Series II : Revisiting Forms-based Authentication

posted Thu 20 Dec 07

Previous articles in this series:
- ASP.NET MVC Dev Series I - Using ASP.NET Membership and Role Providers w/ the MVC Framework

If you've done any secured ASP.NET application development in the past, then you're probably quite familiar with how to rig up URL authorization in your web.config file. Basically, if you're building a forms authentication system (e.g. the users have to supply credentials, they aren't pushed from the client via Windows) then you will undoubtedly have a couple of sections of your site that users can hit anonymously and some sections of the site that users can only hit when authenticated. Furthermore, if you're using a Role provider, you might even have some section of the site that is only available to users with the Admin role assigned to them.

While this stuff all technically works under the ASP.NET MVC framework, it doesn't work as elegantly as I would like. The problem I ran into is that the <location/> element in Web.config doesn't know anything about MVC. All ASP.NET is going to do on your behalf is examine in the incoming requested URL, compare it against the locations defined in Web.config, and determine whether the user has access to that location. If the user doesn't have access to that location, they are usually redirected to a login page.

What happens here is the very nature of the ASP.NET MVC allows me to make my controller routing paths (URLs) dynamic and flexible. It is entirely possible and perhaps even likely that I will be unable to accurately express my secured URL logic using simple hard-coded URLs in <location/> elements. What I really want to do is secure individual methods of my controllers and perhaps entire controllers. For example, if I am building an online banking application and I have a controller called AccountsController, I don't want users to be able to invoke methods on this controller unless they are authenticated. I don't care what URL they used - they shouldn't be able to invoke methods on the controller unless there is an authenticated principal in the context.

Fortunately, we've got some CAS (Code Access Security) attributes that we can decorate our controller classes with that work perfectly well within the context of any (including MVC) ASP.NET application. To ensure that our controller class is never invoked by an anonymous user, we can decorate it as follows:

[PrincipalPermission(SecurityAction.Demand, Authenticated=true)]
public class AccountsController : Controller
{ ... }

Now if we attempt to navigate to any of the URLs that route to this controller ASP.NET will check to make sure that the current principal is indeed authenticated. If I'm using forms authentication, its going to eventually examine my cookies to see if I've got the forms auth cookie stored. Securing my controller class this way allows me to tweak my routing without having to go back and tweak my web.config file. I also like being able to see the security status of each class and method while looking at the class and method. I thoroughly dislike having knowledge separated in multiple locations because that makes it easy for me to forget and neglect said knowledge in my code.

So what happens when you hit a secured controller without the proper credentials? Unfortunately, you're going to get an ASP.NET exception and the default behavior for that is just plain UGLY. There are two ways around this:

Global Security Permission Exception Handling 

Write some code in the Global.asax.cs that checks to see if the root cause of the thrown exception was a security exception. If such is the case, re-direct to your application's login screen:

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError().GetBaseException();
    if (ex is SecurityException)
    {
        Response.Redirect("/Login");
    }
}

Unfortunately, inside the confines of Application_Error, the protection level on RedirectToAction won't let me invoke the method from outside the controller, so I'm stuck using a standard Response.Redirect() here. Since "/Login" is a pretty fixed and constant URL I'm not too worried. If I had multiple potential destinations when a security failure occurred, then I would be concerned about my lack of access to MVC routing logic here.

In-line Security Permission Checking

In this option, instead of decorating the classes with declarative permission demands, the code makes a check imperatively through method invocations. It basically looks something like this:

PrincipalPermission pp = new PrincipalPermission(User.Identity.Name, "Administrator", true);
pp.Demand();

These lines of code will demand that the current user is identified with the given name, is part of the Administrator role, and is authenticated. You would then wrap this in an exception handler and then do your conditional re-direct if the demand failed. While this is a little more explicit, it totally violates DRY (Don't Repeat Yourself). My code would be positively littered with nasty, repetetive security checks.

So for now, until I find something more elegant, I am using the first method. I get the elegance of neatly decorating my controller classes and individual methods with attributes (the attributes have a cleaner syntax than creating instances of the permissions because you have to union them manually for complex assertions) and I get one set of handler code that responds to the security failure.

tags:      

links: digg this    del.icio.us    technorati    reddit

AddThis Social Bookmark Button




1. wayne douglas left...
Wed 16 Jan 08 4:07 pm :: http://www.codingvista.com

Hey

really good post - this is where spring framework and IoC would kick ass.

w://


2. Nik Radford left...
Sun 17 Aug 08 3:27 pm

Have you looked into ActionFilter attrbiutes, introduced in MVC Preview 2? Give them a google, you should be able to do what you want with them :) It's what I use.


Tag Related Posts

Fix for Minor Bug in ASP.NET MVC New Project Template

Mon 04 May 09 2:48 A GMT-05
tags:      

NYC SharePoint Developer Needed

Mon 12 May 08 12:09 P GMT-05

Scott Guthrie Updates the ASP.NET MVC Roadmap

Wed 13 Feb 08 3:49 P GMT-05
tags:    

Volta is to Ajax what Tums is to my Stomach

Wed 30 Jan 08 4:11 P GMT-05

ASP.NET 3.5 Extensions Preview Released

Mon 10 Dec 07 2:10 P GMT-05

Microsoft unveils an MVC framework for ASP.NET

Mon 08 Oct 07 12:58 P GMT-05
tags:      

Exploring the MVC Pattern in WPF

Tue 10 Apr 07 12:51 P GMT-05
tags:                      

ASP.NET Ajax v1.0 Beta

Fri 27 Oct 06 6:17 P GMT-05
tags:      

ASP.NET vs Ruby on Rails : Round 2 (Agility)

Thu 05 Oct 06 11:02 A GMT-05
tags:                      

ASP.NET vs Ruby on Rails : Round 1

Wed 04 Oct 06 1:37 P GMT-05
tags: