Practical Permissions-based Authorization in ASP.NET Core MVC

August 8, 2016

The New Identity framework

As anyone following ASP.NET’s development in the last two years knows, ASP.NET Core has been released and there are a lot of changes. Gone are the days of IIS modules and handlers and the traditional ASP.NET pipeline. Instead, now we have a composable pipeline of delegates. Gone also are System.Web and much of the monolithic frameworks that often were used in web applications. The Identity framework is one that has also changed. Before ASP.NET Core, the Identity framework supported Membership and Roles, where a user could have membership in a given role, and then authorization could be accomplished based on roles. More recently (but also before ASP.NET Core), Identity supported Claims. In the new Identity framework, policy-based authorization was introduced. Policy-based authorization allows for a great deal of flexibility by giving developers a high-level and well-defined way to integrate custom authorization mechanisms into the Identity framework. Read more about policy-based authorization on the ASP.NET Core documentation page.

Resource-based Authorization

In this new, more flexible authorization landscape, Identity also supports the idea of “resource-based” authorization, or in other words, authorization that only applies to a specific web resource. To re-use the example shown in the documentation, imagine you have a “documents” resource in your application (be in API or user-facing web app, it doesn’t matter), where editing is a privileged action:

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authSvc;
    private readonly IDocumentRepository _docRepo;

    public DocumentController(IAuthorizationService authSvc, IDocumentRepository docRepo)
    {
        _authSvc = authSvc;
        _docRepo = docRepo;
    }

    public async Task<IActionResult> Edit(Guid docId)
    {
        var doc = docRepo.Find(docId); // var is type Document

        if (await _authSvc.AuthorizeAsync(User, doc, Operations.Update))
        {
            return View(doc);
        }
        else
        {
            return Challenge();
        }
    }
}

The documentation ably explains the mechanics of what’s going on here, but this is the basis of how you might authorize particular actions.

Permissions

In my application, we have a more complicated authorization model than what can easily be represented by the Microsoft.AspNetCore.Authorization.Infrastructure.OperationAuthorizationRequirement class. We have a fairly complex permissions hierarchy that correspond to various features of the application, some of which are quite fine-grained. At last count, we have 83 specific permissions, and there are many features on our roadmap left to be implemented. We could use the OperationAuthorizationRequirement class, but we would have a great many instances of this class to create and name. While this might be not-terrible for some uses, we also need to be able to represent combinations of our permissions to represent “effective permissions”. It seemed to me that a requirement-per-effective-permission was not going to be practical because the list would already be unwieldy and error prone, and only get worse and quickly. Instead, I needed to write a parameterized requirement like this:

public class PermissionsAuthorizationRequirement : IAuthorizationRequirement
{
    public IEnumerable<Permission> RequiredPermissions { get; }

    public PermissionsAuthorizationRequirement(IEnumerable<Permission> requiredPermissions)
    {
        RequiredPermissions = requiredPermissions;
    }
}

Permission is an enum, and this allows me to pass a list of them as the requirement1. Hopefully it’s apparent how this is more flexible than a instance-per-effective-permission would be, let alone class-per-effective-permission.

Now that I can express combined permissions easily, how can I use this so that I don’t have to manually check each resource and issues challenges?

One way to do this would be to abstract the authorization code into a base class for my authorized controllers, which would transform those lines to something like this:

public async Task<IActionResult> Edit(Guid docId)
{
    var doc = /* ... */

    await RequirePermissions(Permission.OneThing, Permission.AnotherThing));

    return View(doc);
}

This is better, but now I have to pull this base class into the inheritance hierarchy in order to reuse the code, and the authorization mechanism still clutters up my action.

Fortunately, MVC provides a way to wrap actions with some code that doesn’t have to be so intrusive: filters. So I think more ideally, I would be able to write this action method like this:

[RequiresPermissions(Permission.One, Permission.Two)]
public async Task<IActionResult> Edit(Guid docId)
{
    /* ... */
}

In fact, in an earlier iteration of my application, we used a custom authorization filter with exactly these semantics, although it operated outside the Identity framework and idiomatic MVC conventions 2.

Leveraging Dependency Injection

The trick here is that we need the DI container to get access to the IAuthorizationService so that we can actually execute our authorization handler against the right PermissionAuthorizationRequirement. One approach is to use the container as a service locator, although that pattern has become commonly understood to be an anti-pattern because it makes testing much more difficult (or even impossible), and it also means that the type interface of a function or class that relies on it doesn’t accurately describe its behavior, so we’d like to stick with constructor injection. MVC has never supported DI in filters - but it does in ASP.NET Core.

Take a look at the documentation for configuring filters. Buried in there a bit is a demonstration of how to implement a “filter factory” as a filter itself. One that supports DI. So in my case, like this:

public RequiresPermissionAttribute : TypeFilterAttribute
{
    public RequiresPermissionAttribute(params Permission[] permissions)
      : base(typeof(RequiresPermissionAttributeImpl))
    {
        Arguments = new[] { new PermissionAuthorizationRequirement(permissions) };
    }

    private class RequiresPermissionAttributeImpl : Attribute, IAsyncResourceFilter
    {
        private readonly ILogger _logger;
        private readonly IAuthorizationService _authService;
        private readonly PermissionsAuthorizationRequirement _requiredPermissions;

        public RequiresPermissionAttributeImpl(ILogger<RequiresPermissionAttribute> logger,
                                        IAuthorizationService authService,
                                        PermissionsAuthorizationRequirement requiredPermissions)
        {
            _logger = logger;
            _authService = authService;
            _requiredPermissions = requiredPermissions;
        }

        public async Task OnResourceExecutionAsync(ResourceExecutionContext context,
                                                   ResourceExecutionDelegate next)
        {
            if (!await _authService.AuthorizeAsync(context.HttpContext.User,
                                        context.ActionDescriptor.ToString(),
                                        _requiredPermissions))
            {
                context.Result = new ChallengeResult();
            }

            await next();
        }
    }
}

There are a couple of things worth pointing out. Most importantly, you will notice that TypeFilterAttribute is constructed only with knowledge of the type of the private class. That alone will not be enough to construct an instance of RequiresPermissionAttributeImpl because of the requiredPermissions parameter. That won’t come from the DI container because this is a parameterized attribute. Hopefully you’ve notice by now the line in the constructor for RequiresPermissionsAttribute: it sets a property called Arguments. TypeFilterAttribute doesn’t construct its configured type right away. That happens later, and it exposes this Arguments property specifically to allow us to parameterize the type to be constructed. I can easily pass in whatever I like from the attribute annotation itself (within the regular constraints of an attribute of course: const values only, and enums qualify), and then I can set the Arguments property. Once that little bit of plumbing is taken care of, the private class can be written as though it were an attribute exposed like any other, but with the benefit of dependency injection - which means we can avoid anti-patterns and leverage the existing Identity framework mechanisms to plug custom permissions-based authorization. Win-win.


  1. It might occur to you to make the Permission enumeration be a flags enum, but remember: I’m already at 83 permissions. That would already demand more bits than are available in a .NET long. [return]
  2. This isn’t a knock on anyone. We had to do something like this because we had requirements we needed to support and the church in ASP.NET Core (then “ASP.NET vNext”) was high and there were some gaps in the framework that kind of left us on our own. Now that ASP.NET Core is released, we are replacing that mechanism with this one as a part of our RC1 -> 1.0.0 porting effort. [return]