Using the Authorize Attribute to Require Login

The first, simplest step in securing an application is requiring that a user be logged in to access specific URLs within the application. You can do that using the Authorize action filter on either a controller or on specific actions within a controller. The AuthorizeAttribute is the default authorization filter included with ASP.NET MVC. Use it to restrict access to an action method. Applying this attribute to a controller is shorthand for applying it to every action method within the controller.


Authentication and Authorization
Sometimes people get confused with respect to the difference between user authentication and user authorization. It's easy to get these words confused — but in summary, authentication is verifying that users are who they say they are, using some form of login mechanism (username/password, OpenID, and so on — something that says “this is who I am”). Authorization is verifying that they can do what they want to do with respect to your site. This is usually achieved using some type of role-based system.

Without any parameters, the Authorize attribute just requires that the user is logged in to the site in any capacity — in other words, it just forbids anonymous access. You look at that first, and then look at restricting access to specific roles.

Securing Controller Actions

Let's assume that you've naively started on your music store application with a very simple shopping scenario: a StoreController with two actions: Index (which displays the list of albums) and Buy:

using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Wrox.ProMvc4.Security.Authorize.Models;

namespace Wrox.ProMvc4.Security.Authorize.Controllers
{
  public class StoreController : Controller
  {
     public ActionResult Index()
     {
        var albums = GetAlbums();

        return View(albums);
     }

     public ActionResult Buy(int id)
     {
        var album = GetAlbums().Single(a => a.AlbumId == id);

        //Charge the user and ship the album!!!
        return View(album);
     }

     // A simple music catalog
     private static List<Album> GetAlbums()
     {
        var albums = new List<Album>{
           new Album { AlbumId = 1, Title = "The Fall of Math", Price = 8.99M},
           new Album { AlbumId = 2, Title = "The Blue Notebooks", Price = 8.99M},
           new Album { AlbumId = 3, Title = "Lost in Translation", Price = 9.99M },
           new Album { AlbumId = 4, Title = "Permutation", Price = 10.99M },
        };
        return albums;
     }
  }
}

However, you're obviously not done, because the current controller would allow a user to buy an album anonymously. You need to know who the users are when they buy the album. You can resolve this by adding the AuthorizeAttribute to the Buy action, like this:

       [Authorize]
       public ActionResult Buy(int id)
       {
           var album = GetAlbums().Single(a => a.AlbumId == id);

           //Charge the user and ship the album!!!
           return View(album);
       }

To see this code, use NuGet to install the Wrox.ProMvc4.Security.Authorize package into a default ASP.NET MVC project, as follows:

Install-Package Wrox.ProMvc4.Security.Authorize

Run the application and browse to /Store. You'll see a list of albums, and you haven't had to log in or register at this point, as shown in Figure 7.1.

When you click the Buy link, however, you are required to log in (see Figure 7.2).

Since you don't have an account yet, you'll need to click the Register link, which displays a standard account signup page (see Figure 7.3).

When you click the Buy button after registering, the authorization check passes and you're shown the purchase confirmation page, as shown in Figure 7.4. (Of course, a real application would also collect some additional information during the checkout, as demonstrated in the MVC Music Store application.)


Product Team Aside
A common means of securing an application with Web Forms is to use URL authorization. For example, if you have an admin section and you want to restrict it to users who are in the Admins role, you might place all your admin pages in an admin folder and deny access to everyone except those in the Admins role to that subfolder. With ASP.NET Web Forms, you can secure a directory on your site by locking it down in the web.config:
<location path=”Admin” allowOverride=”false”>
<system.web>
  <authorization>
    <allow roles=”Administrator” />
    <deny users=”?” />
  </authorization>
</system.web>
</location>
With MVC that approach won't work so well for two reasons:
  • Requests no longer map to physical directories.
  • There may be more than one way to route to the same controller.
With MVC, it is possible in theory to have an AdminController encapsulate your application's administrative functionality and then set URL authorization within your root web.config file to block access to any request that begins with /Admin. However, this isn't necessarily secure. It may be possible that you have another route that maps to the AdminController by accident.
For example, say that later on you decide that you want to switch the order of {controller} and {action} within your default routes. So now, /Index/Admin is the URL for the default admin page, but that is no longer blocked by your URL authorization.
A good approach to security is to always put the security check as close as possible to the thing you are securing. You might have other checks higher up the stack, but ultimately, you want to secure the actual resource. This way, no matter how the user got to the resource, there will always be a security check. In this case, you don't want to rely on routing and URL authorization to secure a controller; you really want to secure the controller itself. The AuthorizeAttribute serves this purpose.
  • If you don't specify any roles or users, the current user must simply be authenticated in order to call the action method. This is an easy way to block unauthenticated users from a particular controller action.
  • If a user attempts to access an action method with this attribute applied and fails the authorization check, the filter causes the server to return a “401 Unauthorized” HTTP status code.
  • In the case that forms authentication is enabled and a login URL is specified in the web.config, ASP.NET will handle this response code and redirect the user to the login page. This is an existing behavior of ASP.NET and is not new to ASP.NET MVC.

How the AuthorizeAttribute Works with Forms Authentication and the AccountController

What's going on behind the scenes here? Clearly, we didn't write any code (controllers or views) to handle the Log On and Register URLs, so where did it come from? The ASP.NET MVC Internet Application template includes an AccountController that implements support for accounts managed by both ASP.NET Membership and OAuth authentication.

The AuthorizeAttribute is a filter, 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, you can see that the underlying security check is 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 action result is returned, which produces an HTTP 401 (Unauthorized) status code. This 401 status code is intercepted by the FormsAuthenticationModule OnLeave method, which instead redirects to the application login page defined in the application's web.config, as shown here:

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

This redirection address includes a return URL, so after completing login successfully, the Account / LogOn action redirects to the originally requested page.


Open Redirection as a Security Vector
The login redirection process is a target for open redirection attacks because attackers can craft malicious post-login URLs, which could redirect users to harmful websites. This threat is discussed later in this chapter.
It's nice that the AccountController — and its associated views — are all provided in the ASP.NET MVC Internet Application template. 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.
  • 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.

Windows Authentication in the Intranet Application Template

The Intranet Application template (available in ASP.NET MVC 3 Tools Update and later) is very similar to the Internet Application template, with one exception: It replaces Forms Authentication with Windows Authentication.

Because Registration and Log On with Windows Authentication are handled outside of the web application, this template doesn't require the AccountController or the associated models and views. To configure Windows Authentication, this template includes the following line in web.config:

<authentication mode="Windows" />

This template also includes a readme.txt file with the following instructions on how to configure Windows Authentication in both IIS and IIS Express. In order to use the Intranet template, you'll need to enable Windows authentication and disable Anonymous authentication.

IIS 7 and IIS 8

1. Open IIS Manager and navigate to your website.
2. In Features View, double-click Authentication.
3. On the Authentication page, select Windows authentication. If Windows authentication is not an option, you'll need to make sure Windows authentication is installed on the server.
To enable Windows authentication in Windows:
a. In the Control Panel, open Programs and Features.
b. Select Turn Windows features on or off.
c. Navigate to Internet Information Services Í World Wide Web Services Í Security and make sure the Windows authentication node is checked.
To enable Windows authentication on Windows Server:
a. In the Server Manager, select Web Server (IIS) and click Add Role Services.
b. Navigate to Web Server Í Security and make sure the Windows authentication node is checked.
4. In the Actions pane, click Enable to use Windows authentication.
5. On the Authentication page, select Anonymous authentication.
6. In the Actions pane, click Disable to disable anonymous authentication.

IIS Express

1. Click on your project in the Solution Explorer to select the project.
2. If the Properties pane is not open, open it (F4).
3. In the Properties pane for your project:
a. Set Anonymous Authentication to Disabled.
b. Set Windows Authentication to Enabled.

Securing Entire Controllers

The preceding scenario demonstrated a single controller with the AuthorizeAttribute applied to specific controller actions. After some time, you realize that the browsing, shopping cart, and checkout portions of your website each deserve separate controllers. Several actions are associated with both the anonymous Shopping Cart (view cart, add item to cart, remove from cart) and the authenticated Checkout (add address and payment information, complete checkout). Requiring Authorization on Checkout lets you transparently handle the transition from Shopping Cart (anonymous) to Checkout (registration required) in the Music Store scenario. You accomplish this by putting the AuthorizeAttribute on the CheckoutController, like this:

[Authorize]
public class CheckoutController : Controller

This says that all actions in the CheckoutController will allow any registered user, but will not allow anonymous access.

Securing Your Entire Application Using a Global Authorization Filter

For many sites, nearly the entire application should require authorization. In this case, it's simpler to require authorization by default and make exceptions in the few places where anonymous access is allowed — such as the site's home page and URLs required for the login process. For this case, it's a good idea to configure the AuthorizeAttribute as a global filter and allow anonymous access to specific controllers or methods using the AllowAnonymous attribute.

To register the AuthorizeAttribute as a global filter, add it to the global filters collection in RegisterGlobalFilters:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
    filters.Add(new System.Web.Mvc.AuthorizeAttribute());
    filters.Add(new HandleErrorAttribute());
}

This will apply the AuthorizeAttribute to all controller actions in the application. The obvious problem this presents is that it restricts access to the entire site, including the AccountController. Prior to MVC 4, if you wanted to use a global filter to require authorization, you had to do something special to allow anonymous access to the AccountController. A common technique was to subclass the AuthorizeAttribute and include some extra logic to selectively allow access to specific actions. MVC 4 adds a new AllowAnonymous attribute. You can place the AuthorizeAttribute on any methods (or entire controllers) to opt out of authorization as desired.

For an example, you can see the default AccountController in a new MVC 4 application using the Internet Application template. All methods that would require external access if the AuthorizeAttribute were registered as a global filter are decorated with the AllowAnonymous attribute. For example, the Login HTTP Get action appears, as follows:

//
// GET: /Account/Login

[AllowAnonymous]
public ActionResult Login()
{
       return ContextDependentView();
}

This way, even if you register the AuthorizeAttribute as a global filter, users will be able to access the login actions.


Global Authorization Is Global Only to MVC
It's important to keep in mind that a global filter applies only to MVC controller actions. It doesn't secure Web Forms, static content, or other ASP.NET handlers.
As mentioned earlier, Web Forms and static resources map to file paths and can be secured using the authorization element in your web.config. ASP.NET handler security is more complex; like an MVC action, a handler can map to multiple URLs.
Securing handlers is normally handled via custom code in the ProcessRequest method. For example, you may check User.Identity.IsAuthenticated and redirect or return an error if the authentication check fails.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset