65.9K
CodeProject is changing. Read more.
Home

ASP.NET MVC - AuthorizeWithExemptionsAttribute

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.57/5 (5 votes)

Aug 23, 2010

CPOL

2 min read

viewsIcon

33991

downloadIcon

475

This base controller will secure all your actions except those which will be marked as UnsecuredAction.

Introduction

In this article, I'll provide you with a solution for securing ASP.NET MVC application's controllers in a way that all the actions are secured except those you define as unsecure (by default, all the actions are unsecure unless you define them as secure).

I encountered a problem in the ASP.NET MVC authorization model, which is built as opt-in. You can secure an action or a controller using the [Authorize] attribute. The problem is that if you want to secure a whole controller and make some actions in that controller as not secured - you can't.

This article will explain to you how you can secure a whole controller and make some actions as not secured within it.

This project was written in Visual Studio 2010 as an ASP.NET MVC 2 project.

Background

I was looking for a solution to that problem, and couldn't find a reasonable one. Other solutions suggested to:

  • Move the unsecured actions to another controller (not secured)
  • Write the AuthorizeCore method to be dependent on the Action's names
  • Put the [Authorize] attribute on all the actions except the non-secured actions

In this article, I'll provide you with my solution to this problem.

Using the Code

To use the code, you'll need your controllers to put any [CustomAuthorize] attribute which inherits from AuthorizeWithExemptionsAttribute on your controllers. Calling any action on this controller will include an authorization check. If you want to mark an Action as non-secured, just put the [UnsecuredAction] Attribute above it.

Explanation

The core of this solution is in the [AuthorizeWithExemptions] Attribute in the OnAuthorization method. This method checks if the called Action has the UnsecuredActionAttribute, and if it does, it marks filterContext.HttpContext.SkipAuthorization as true.

public override void OnAuthorization(AuthorizationContext filterContext)
{
    ActionDescriptor action = filterContext.ActionDescriptor;
    bool IsUnsecured = action.GetCustomAttributes(
                         typeof(UnsecuredActionAttribute), true).Count() > 0;

    //If doesn't have UnsecuredActionAttribute - then do the authorization
    filterContext.HttpContext.SkipAuthorization = IsUnsecured;

    base.OnAuthorization(filterContext);
}

Then, CustomAuthorizeAttribute performs the authorization check only if SkipAuthorization is false.

protected override bool AuthorizeCore(HttpContextBase httpContext)
{
  if (httpContext.SkipAuthorization)
  {
      return true;
  }

  // Do any authorization logic here
  return httpContext.Request.QueryString["password"] == "password";
}

* Of course, never provide the password in the QueryString, it was made like that just for the ease of the example.

So now, when you try to invoke the secured controller, you get the HTTP error code 401: Unauthorized.

And the unsecured controller works without any authorization required.

History

  • 23rd August, 2010: Initial post
  • 24th August, 2010: Updated article and source code