Looking at how the ASP.NET MVC Authorize interacts with ASP.NET Forms Authorization

Background: The Authorization flow in a default ASP.NET MVC Internet Application

ASP.NET MVC includes an [Authorize] attribute, which when placed on any controller actions will forbid unauthorized access. The AuthorizeAttribute allows you to specify a list of roles or users, like this:


[Authorize(Roles="CEO,HR")]
public ActionResult FireEmployee(int id)
{
    var employee = GetEmployees().Single(e => a.EmployeeId == id);

    //TODO: Fix the glitch

    return View(employee);
}

You can also place the AuthorizeAttribute on a controller, in which case it will apply to all actions in the controller. Attempting to access an action secured by the AuthorizeAttribute when you're not logged in will take you to a standard LogOn screen, with a link to register if you don't already have an account.

2011-04-23 23h45_12

You don't have to write this code, because the ASP.NET MVC 3 Internet Application template includes a basic AccountController which implements the following actions (along with the associated models and views):

  • LogOn
  • Register
  • ChangePassword / ChangePasswordSuccess

How does the [Authorize] attribute redirect me to Log On?

The AuthorizeAttribute is an ActionFilter, which means that it can execute before the associated controller action. The AuthorizeAttribute performs its main work in the OnAuthorization method, which is a standard method defined in the IAuthorizationFilter interface. Checking the MVC source code, we can see that the underlying security check is really just looking at the underlying authentication information held by the ASP.NET context:


IPrincipal user = httpContext.User;

if (!user.Identity.IsAuthenticated)
{
    return false;
}

If the user fails authentication, an HttpUnauthorizedResult ActionResult is returned, which produces an HTTP 401(Unauthorized) status code. If it weren’t for ASP.NET Forms Authentication, an HTTP 401 status code would be sent to the browser, which would show the browser’s default login prompt. You can get an idea using the (very useful) httpstat.us site, browsing to http://httpstat.us/401

2011-04-28 16h08_23

These 401 prompts are uniformly hideous, but the real problem is that they aren’t able to authenticate against membership systems running on your server. That’s why most websites prefer to display a nice login web form (as shown in the first login screenshot). As mentioned earlier, that’s where ASP.NET Forms Authentication comes into play.

How ASP.NET Forms Authentication turns MVC’s 401 errors into a redirect to a login page

Your application’s web.config contains the settings you’re most likely to need to edit. There are a lot of other default values living in the Framework’s web.config file. On my default installation, that file is found at C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config. If you crack that open, you’ll see that there are 14 default HTTP Modules installed. One of them is the FormsAuthenticationModule. The code for the FormsAuthenticationModule is found in System.Web.dll.

The FormsAuthenticationModule OnLeave method has one purpose: it checks for 401 responses and turns them into redirects. Before it redirects, though, it figures out a return url so so after completing login successfully, the Account / Logon action redirects to the originally requested page.

The login url is defined in the application’s web.config, as shown below.


<authentication mode="Forms">
    <forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>

You can of course change this login url to any custom location that makes sense for you.

The AccountController LogOn method is a standard ASP.NET MVC controller action. There’s not a lot of code here, since it’s leveraging the underlying membership provider mechanism in ASP.NET.


[HttpPost]
public ActionResult LogOn(LogOnModel model, string returnUrl)
{
	if (ModelState.IsValid)
	{
		if (Membership.ValidateUser(model.UserName, model.Password))
		{
			FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
			if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
				&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
			{
				return Redirect(returnUrl);
			}
			else
			{
				return RedirectToAction("Index", "Home");
			}
		}
		else
		{
			ModelState.AddModelError("", "The user name or password provided is incorrect.");
		}
	}

	// If we got this far, something failed, redisplay form
	return View(model);
}

This is all configurable

It’s nice that this is all provided in an ASP.NET MVC the Internet Application and Intranet Application templates. In simple cases, adding authorization doesn’t require any additional code or configuration.

Equally nice, though, is that you can change any of those parts:

  • The AccountController (as well as the associated Account models and views) is a standard ASP.NET MVC controller, which is pretty easy to modify. You can see an example of this in the MVC Music Store tutorial, in which we migrate an anonymous shopping cart to a user account on registration / login.
  • The authorization calls work against the standard ASP.NET Membership provider mechanism, as defined in your web.config <authorization> setting. You can switch providers, or write your own.
  • The AuthorizeAttribute is a standard authorization attribute, implementing IAuthorizeFilter. You can create your own authorization filters.

10 Comments

  • The problem with using AuthorizeAttribute is that it will always redirects you to Login page even if you are authenticated user(but not allow to run the action). So you need custom Authorize filter. Here is one created by Levi, http://forums.asp.net/p/1573254/3948388.aspx

  • How about talking about authentication/caching on a garden/farm environment :)

  • Controller - level Authorize doesn't override the Action, it's still the other way around... But the user has to be able to access the Controller first, then it'll check access to the Action.

    Can you let someone try on some clothes without first letting them into the store?

  • Hi Jon,
    I wanna learn that why attributes don't work while testing.

    Because when I test an action mothod of a controller, which controllercontext.user is not authenticated and signed [Authorize] attribute, redirect error does not occure test project.

    what is the solution?

  • Audio engineering is a promising job that provides immense option in film, video clip manufacturing, audio broadcasting and advertising. Audio engineers use a number of recording devices these kinds of as large-tech microphones that are needed to file even the slightest variations of sound.

    The general idea of engineering relates to setting up bridges, properties, roads and very similar variety of work. Most people do not think of audio engineering as a sort of regular engineering. Having said that, it is a single of the best having to pay work opportunities and has lots of productive people today performing in the field.

    Audio conferencing has never had a technologies champion. The sector leader historically was the lethargic AT&T whose as soon as revolutionary Bell Labs dabbled with internet technologies and then broke apart. The Baby Bells carried on this tradition. Therefore, the industry has certainly not had an innovative advocate. A new breed of entrepreneurial innovators is beginning a populist revolution.

  • just add [Authorize] to index action of HomeController, u can check Everyone before letting into you shop...

  • I leave a response when I like a post on a website or if I have something to contribute to
    the discussion. It's caused by the passion communicated in the article I browsed. And after this article Looking at how the ASP.NET MVC Authorize interacts with ASP.NET Forms Authorization - Jon Galloway. I was excited enough to write a thought :-) I actually do have 2 questions for you if it's okay.
    Could it be only me or does it look as if like some of these
    comments appear as if they are left by brain dead people? :
    -P And, if you are posting at additional places, I'd like to keep up with everything fresh you have to post. Could you list the complete urls of all your social sites like your Facebook page, twitter feed, or linkedin profile?

  • I'm not sure exactly why but this website is loading extremely slow for me. Is anyone else having this issue or is it a problem on my end? I'll
    check back later and see if the problem still exists.

  • I've been exploring for a little for any high quality articles or blog posts on this kind of space . Exploring in Yahoo I ultimately stumbled upon this web site. Studying this info So i am satisfied to convey that I've an incredibly excellent uncanny feeling I discovered just what I needed.
    I such a lot definitely will make certain to don?t put out of
    your mind this website and provides it a look on a relentless basis.

  • Balance, apparently learning about directing developer of online pokies australia in the
    UK.

Comments have been disabled for this content.