ASP.NET Core Custom Policy Based Authorization

In the last article, I've demonstrated the cookie authentication and policy based authorization. We're going to build on that knowledge and learn about the custom policy based authorization. Let's start.

If you recall, in Startup.cs class we've added the following configuration to enable policy based authorization:

public void ConfigureServices(IServiceCollection services)  
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ReadPolicy", policyBuilder =>
        {
            policyBuilder.RequireAuthenticatedUser()
                .RequireAssertion(context => context.User.HasClaim("Read", "true"))
                .Build();
        });
    });
}

The policy requires the user to have a Read claim. This works fine, but the code can get pretty complex very fast based on our business requirements.

The alternative option is to extract the authorization logic into a custom policy.

In order to demonstrate the use case, let's change our authorization requirement. We now require users to have an active license which is determined by the LicenseExpireDate claim. However, if the license has expired within 30 days, we'll still continue to allow users to have access.

Let's implement the custom policy for our authorization requirement.

First we need define the IAuthorizationRequirement. An authorization requirement is parameter(s) to authorization handler that we will use to evaluate the current user principal:

public class ActiveLicenseRequirement : IAuthorizationRequirement  
{
    public ActiveLicenseRequirement(int gracePeriod)
    {
        GracePeriod = gracePeriod;
    }

    protected int GracePeriod { get; set; }
}

If you do not need to pass any external data (in our case we do), A requirement doesn't need to have data or properties.

Next, we need to define the AuthorizationHandler where the authorization logic will reside:

public class LicenseExpireDateHandler : AuthorizationHandler< ActiveLicenseRequirement>  
{
    protected override Task HandleRequirementAsync(AuthorizationContext context, LicenseExpireDate requirement)
    {
        var claim = context.User.FindFirst("LicenseExpireDate");
        var expireDate = DateTime.Parse(claim?.Value).AddDays(requirement.GracePeriod).Date

        if (expireDate <= DateTime.Now.Date)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Authorization Handlers support dependency injection. That means, If you need to evaluate some business rules coming from an external repository, service, etc., you can inject it to the authorization handler constructor as long as the it is registered in the services collection.

In our authorization handler, we simply evaluate the user's LicenseExpireDate claim and ensure they have an active license. If the condition is met, we then mark the authorization successful. Otherwise the authorization marked as unauthorized (403, Forbidden).

Next we need to register authorization handler in the services collection:

public void ConfigureServices(IServiceCollection services)  
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ActiveLicense",
                          policy => policy.Requirements.Add(new ActiveLicenseRequirement(30)));
    });

    services.AddSingleton<IAuthorizationHandler, LicenseExpireDateHandler>();
}

Note that as part of the AddPolicy call, we're passing our requirement with the gracePeriod of 30 days.

All we have left to do is update our Authorization attribute with the new policy:

[Authorize(Policy = "ActiveLicense")]
public IActionResult Index()  
{
    return View();
}

In the next article, I will cover the Resource Based Authorization.

Arif Yayalar

Software Engineer

Seattle, WA

Subscribe to {:arif, :yayalar}

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!