Authorization Options in ASP.NET Core

ASP.NET Core 2.x has a robust authorization system extending beyond just the age old [Authorize] attribute. With ASP.NET Core you now have access to Authorization policies and Authorization filters.

To understand what they are and when to use them (and when not to), you should begin by familiarizing yourself with how authorization works in MVC and where it fits into the context of ASP.NET Core middleware as a whole.

ASP.NET Core Middleware Pipeline

In a brand new ASP.NET Core 2.0 MVC project (using built-in templates), the request middleware pipeline executes in this order:

  1. Things like add logging, UseStaticFiles, UseDeveloperExceptionPage when in Development mode, etc.
  2. UseAuthentication() – the Identity middleware that will SET the HttpContext.User object to a ClaimsPrincipal with one or more identities.
  3. *** A good spot to put pre-MVC code that acts on the current identity. Though be sure to set a breakpoint to ensure your code is being hit if depending on authentication schemes. UseMvc() middleware will then do its thing:
    1. It will first do routing and try to map the request to an action. If this doesn’t happen it will return a 404 and your Authorization filters and policies will not be executed.
    2. Next it will run through the various filter types, beginning with Authorization filters. (for others see https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-2.0). It is important to note that your Authorization filter will execute only on requests that map to a valid Controller and Action method, prior to that method executing.
    3. If it finds an action with an [Authorize] attribute on it, it will then execute the current policy, which is the sequence of the following:
      1. The default policy (See Setting Default Policy below) .
      2. Policies, Roles, and AuthenticationSchemes set as properties on the Authorize attribute.

Which Authorization Method? Some Guidelines…

If all that sounds confusing, here are some simple guidelines:

If you want code to execute on every request it must be in the middleware pipeline directly. There is no guarantee that a request will get to the MVC middleware much less successfully route to an action, so if it must execute put it here. You can see slides and code from my ASP.NET Core middleware session on Github. If I later write corresponding blog content I will update this post with its location.

If you want code to execute on every request prior to entering an MVC action (including actions allowing anonymous access!), you want to create an Authorization filter (see Creating an Authorization Filter below).

If you want code to execute prior to entering only protected actions (actions that have an Authorize attribute for example), you will want to set a default policy (see Setting Default Policy below).

If none of those options work, you can create an authorization policy and set it on each action (or controller) individually providing more fine-grained control (see Using Authorization Policies below).

Creating an Authorization Filter

The authorization filter will be executed on any request that enters the MVC middleware and maps to a valid action. Here is an example of how to create an authorization filter:

[csharp]
// Startup.cs
// public void
ConfigureServices(IServiceCollection services)
var globalPolicy = new AuthorizationPolicyBuilder()
.AddRequirements(new LocationRequirement() {
Location = Location.FromRegions(Region.SouthAmerica)
})
.Build(); services.AddMvc(config => {
config.Filters.Add(new AuthorizeFilter(globalPolicy));
});
[/csharp]

Setting Default Policy

The default policy will only be executed on requests prior to entering protected actions such as those wrapped by an [Authorize] attribute.

[csharp]
// Startup.cs
// public void
ConfigureServices(IServiceCollection services)
var defaultPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.AddRequirements(new AccountStatusRequirement() {
AccountStatus = AccountStatuses.Open
}) .RequireClaim(“login-source”, “intranet”, “vpn”)
// A .Build(); services.AddAuthorization(options => options.DefaultPolicy = defaultPolicy);
[/csharp]

Using Authorization Policies

For more fine-grained control over when policies are executed, you can use authorization policies. First define them like this:

[csharp]
// Startup.cs
// public void
ConfigureServices(IServiceCollection services) services.AddAuthorization(options => {
builder .RequireAuthenticatedUser() .RequireClaim(“login-source”, “intranet”);
}));
[/csharp]

And then use them by using the [Authorize] attribute on a controller or action like this:

[csharp]
[Authorize(Policy = “InOfficeOnly”)]
public IActionResult Index()
{

}
[/csharp]