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;
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;
}
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