Why does AuthorizeAttribute redirect to the login page for authentication and authorization failures?

When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing –
older revisions of the HTTP specification used status code 401 for both “unauthorized” and “unauthenticated”.

From the original specification:

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

In fact, you can see the confusion right there – it uses the word “authorization” when it means “authentication”. In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It’s unlikely the user would have a second set of credentials that would give them access – bad user experience all around.

Consider most operating systems – when you attempt to read a file you don’t have permission to access, you aren’t shown a login screen!

Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.

From “Hyper Text Transport Protocol (HTTP/1.1): Authentication” (RFC 7235):

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

From “Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content” (RFC 7231):

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.

Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect – the HTTP/1.1 specification was fixed.

Rather than attempt to change ASP.NET’s login page redirects, it’s easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute) in your website’s default namespace (this is very important) then the compiler will automatically pick it up instead of MVC’s standard one. Of course, you could always give the attribute a new name if you’d rather take that approach.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

Leave a Comment