• Home
  • Tutorials
  • Development Tools
  • Contact Us

Developing Software

Mastering Software Craftsmanship

Improving the Authorize Attribute in ASP.NET MVC

16th January 2015 by @developingsoft

The odd thing I’ve found with the AuthorizeAttribute, is how it redirects you to the sign in page, even when authenticated with a role that doesn’t have access to the controller or action. As you can imagine, Joe Blogs (the basic user) might feel a tad confused, when presented with a sign in page when he’s already signed in.

Wouldn’t it be kinder to tell Joe why he’s not special, and can’t view that super duper page meant for the powers that be?

Why the Authorize attribute always redirects to the sign in page?

If you open up the AuthorizeAttribute with .NET Reflector, or even easier, look at the source code of ASP.NET MVC 5 on GitHub, you will see three possible conditions that cause the attribute to return a HttpUnauthorizedResult.

I have provided a snippet of the code below with comments pointing out each condition:

protected virtual bool AuthorizeCore(HttpContextBase httpContext)
{
    if (httpContext == null)
    {
        throw new ArgumentNullException("httpContext");
    }

    // Condition 1: User is not authenticated
    IPrincipal user = httpContext.User;
    if (!user.Identity.IsAuthenticated)
    {
        return false;
    }

    // Condition 2: User is authenticated but does not have the correct name
    if (_usersSplit.Length > 0 && !_usersSplit.Contains(user.Identity.Name, 
        StringComparer.OrdinalIgnoreCase))
    {
        return false;
    }

    // Condition 3: User is authenticated but does not have the correct role
    if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
    {
        return false;
    }

    return true;
}

All three conditions return false which makes the OnAuthorization method call HandleUnauthorizedRequest.

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new HttpUnauthorizedResult();
}

This method sets the action methods result to a new instance of HttpUnauthorizedResult. This sets the status code to 401 Unauthorized, causing the forms authentication module to redirect the user to the sign in page. In my opinion, it’s better to return a 403 Forbidden status code for conditions 2 and 3. There is a very simple way of doing this, which I will explain below.

CustomAuthorizeAttribute that returns HttpForbiddenResult

The first step is to create a new class called HttpForbiddenResult which is used by the the custom attribute to set the status code to 403 forbidden;

public class HttpForbiddenResult : HttpStatusCodeResult
{
    public HttpForbiddenResult()
        : this(null)
    {
    }

    public HttpForbiddenResult(string statusDescription)
        : base(HttpStatusCode.Forbidden, statusDescription)
    {
    }
}

Next we need to create the CustomAuthorizeAttribute and override the HandleUnauthorizedRequest method so that it looks like the following:

public class CusomAuthorizeAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new HttpForbiddenResult();
        }
        else
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }
}

As you can see from the code above, if the user is authenticated but does not belong to the role applied to the attribute, the HttpForbiddenResult will be applied to the AuthorizationContext.Result instead of the HttpUnauthorizedResult.

How to display the error page

If you apply the CustomAuthorizeAttribute to an action, and trigger a 403 forbidden response, you will see the following page is displayed:

403 Forbidden Response

This is still not a very helpful message, so to get around this we need to modify the Web.config so that it loads an action from an ErrorController.

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="403" />
    <error statusCode="403" responseMode="ExecuteURL" path="/error/forbidden" />
    </httpErrors>
</system.webServer>

Now all you need to do is make sure there is an ErrorController with a Forbidden action and a View that displays a useful error message.

Joe is gonna be so pleased 🙂

Share this on:

Filed Under: Tutorials Tagged With: ASP.NET MVC, C#

Search

Advertisement

Newsletter

Subscribe now to receive practical tips on how to become a better software developer.

Free - No Spam - 100% Email Privacy

Featured Posts

Abstract Factory Pattern: C# Example Using the Unity Game Engine

23 Software Design Patterns That Will Make You a More Effective Programmer

How to Deploy an ASP.NET Core Website to Ubuntu with Git

How to Run an ASP.NET Core Website in Production on Ubuntu Linux

How to Install the Edimax Wireless nano USB Adapter on Windows IoT Core for Raspberry Pi

How to Convert a Post Title into a Friendly URL (Slug) in C#

How to Convert Markdown to HTML in ASP.NET Core

How to Send an E-Mail with ASP.NET Core and Mailgun

How to Generate a Sitemap in ASP.NET MVC and ASP.NET Core

How to Create an MD5 Hash of a String in C# and Displaying a Gravatar Image

© 2014–2023 Developing SoftwareTerms • Privacy