Role Based Access Control in ASP.NET MVC

Role Based Access Control in ASP.NET MVC is pretty straight forward. There is also a way to do Claims access control, but the most common way is the authorization of a user based on the roles they have in an organization.

This blog post only explains RBAC using ASP.NET Model-View-Controller framework for web applications.

As a developer, to show or hide action links in a View, depending on the user role you can use the following Razor syntax:

@if (User.IsInRole("Administrator"))
{
...
}

On the Controller class, to avoid access to an action if the user types in the URL directly on the browser, we can annotate the action with the Role check tags.

For example, the following code would limit access to any actions on the AdministrationController to users who are  members of the Administrator group.

[Authorize(Roles = "Administrator")]
public class AdministrationController : Controller
{
}

You can specify multiple roles as a comma separated list;

[Authorize(Roles = "HRManager,Finance")]
public class SalaryController : Controller
{
}

The SalaryController  class above will be only accessible by users who are members of the HRManager role or theFinance role.

If you apply multiple attributes then, a user’s HTTP request, accessing the methods on the controller  must be a member of all the roles specified. The following sample requires that a user must be a member of both the PowerUser and ControlPanelUser role before authorization is granted.

[Authorize(Roles = "PowerUser")]
[Authorize(Roles = "ControlPanelUser")]
public class ControlPanelController : Controller
{
}

You can further limit access by applying additional role authorization attributes at the action level;

[Authorize(Roles = "Administrator, PowerUser")]
public class ControlPanelController : Controller
{
    public ActionResult SetTime()
    {
    }
 
    [Authorize(Roles = "Administrator")]
    public ActionResult ShutDown()
    {
    }
}

In the previous code snippet members of the Administrator role or the PowerUser role can access the controller and the SetTime action, but, only members of the Administrator role can access the ShutDown action.

You can also lock down a controller but allow anonymous, unauthenticated access to individual actions.

[Authorize]
public class ControlPanelController : Controller
{
    public ActionResult SetTime()
    {
    }
 
    [AllowAnonymous]
    public ActionResult Login()
    {
    }
}

There is also a way to use Policies for limiting access, but to keep it simple, since we already have the roles defined, we can use the common RBAC for now until we need something more complex.

When the user requests the URL directly they will get a nasty 401 Unauthorized page from IIS if their request is not Authorized.

The Razor code shown on the first code snippet can be used in the View to show the elements on the View, if the User is part of the Administrators role, but if the requestor (user) is not part of the role, he or she will receive a 401 HTTP Unauthorized response.

We can give them a more friendly page explaining they don’t have permissions to access the resources requested and link them to a request access page.

This is what could be done:

For a 401 you will probably be seeing the standard 401 Unauthorized page, even if you have added 401 to the customerrors section in your web.config. When using IIS and Windows integrated Authentication, the check happens before ASP.NET MVC even sees the request.

By editing the Global.asax file you can redirect to a route created for 401 Unauthorized HTTP response errors, sending the user to the “Unauthorized to see this” View (friendly page). The use case for this scenario would be if someone received a link for a View that requires the user to be authorized but  the user has not completed other steps in the process, such as paperwork needed prior to accessing the secure resource.

In the Global.asax:

void Application_EndRequest(object sender, System.EventArgs e)
{
    // If the user is not authorized to see this page or access this function, send them to the error page.
    if (Response.StatusCode == 401)
    {
        Response.ClearContent();
        Response.RedirectToRoute("ErrorHandler", (RouteTable.Routes["ErrorHandler"] as Route).Defaults);
    }
}

and in the Route.config:

     routes.MapRoute(
               "ErrorHandler",
               "Error/{action}/{errMsg}",
                new { controller = "Error", action = "Unauthorized", errMsg = UrlParameter.Optional }
     );

and in the ErrorController class:

public ViewResult Unauthorized()
{
        //Response.StatusCode = 401; 
        // Do not set this or else you get a redirect loop
        return View();
        //where View is the friendly .cshtml page
}

 

Voila!

Happy coding.