Security authorization
.NET Core ASP.NET Core ASP.NET Core Identity OAuth Visual Studio

How to Use Claim Based Authorization in .NET Core Web Applications

Welcome to today’s post.

I will be discussing the implementation of imperative claims-based authorization handlers.

To be able to use claims-based authorization, we require the following prerequisites for the application security:

  1. Existing user accounts.
  2. Additional claims for each user account.

User accounts do NOT need to be in roles to utilize claims-based authorization. They can be used in isolation or in conjunction with role-based authorization.

In a previous I showed how to implement role-based imperative authorization.

In this blog I will show you how to use claims-based imperative authorization.

In the first section, I will show how to create authorization requirements that will then be used within authorization policies.

Creating Authorization Requirements

In an online library application, suppose we have a policy that prevents non-adults from loaning books that are for adults. Call this policy BookLoanAgeRestriction.

We also define a requirement for the minimum age, MinimumAgeRequirement.

Define the age requirement in a class as follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

namespace BookLoan.Authorization
{
    public class MinimumAgeRequirement: IAuthorizationRequirement 
    {
        public int MinimumAge { get; }

        public MinimumAgeRequirement(int minimumAge)
        {
            MinimumAge = minimumAge;
        }
    }
}

In the startup ConfigureServices(), declare the policy as follows:

public void ConfigureServices(IServiceCollection services)
{
   ..
   services.AddAuthorization(options =>
   {
      ..
      options.AddPolicy("BookLoanAgeRestriction", policy =>
      {
         policy.AddRequirements(
            new MinimumAgeRequirement(18));
         });
      });
      ..
      services.AddSingleton<IAuthorizationHandler,
         BookLoanAgeRestrictionHandler>();
}

The age limit has been hard-coded for illustrative purposes, however it is better to configure this within your application.

The BookLoanAgeRestrictionHandler class that I have declared of type IAuthorizationHandler can then be used within an API method to test if the current user satisfies the age requirement of the book that is retrieved from the library data store. I will show how it is defined in the next section.

Creating the Authorization Claim Policy Handler

In this section, I will show how the claim policy handler is implemented.

The authorization handler is a method defined within the class type:

AuthorizationHandler<MinimumAgeRequirement, BookViewModel>

It makes us of both the properties within both the MinimumAgeRequirement class, the BookViewModel DTO/model and the DateOfBirth claim of the current user to determine the age requirement in relation to the age rating of the book.

The handler is implemented as shown:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using System.Security.Claims;
using BookLoan.Authorization;
using BookLoan.Models;
using BookLoan.Services;

namespace BookLoan.Authorization
{
    public class BookLoanAgeRestrictionHandler : AuthorizationHandler<MinimumAgeRequirement, BookViewModel>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement, BookViewModel resource)
        {
            if (!context.User.HasClaim(c => 
               c.Type == ClaimTypes.DateOfBirth))
            {
                return Task.CompletedTask;
            }
            var dateOfBirth = Convert.ToDateTime(
                context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth).Value); 
            int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
            if (dateOfBirth > 
               DateTime.Today.AddYears(-calculatedAge))
            {
                calculatedAge--;
            }
            if (resource.Genre.Contains("Adult"))
            {
                if (calculatedAge >= requirement.MinimumAge)
                {
                    context.Succeed(requirement);
                }
            }
            else
                context.Succeed(requirement); 
            return Task.CompletedTask;
        }
    }
}

The handler method contains the following parameters:

context. This is the context of the user accessing the handler.

requirement. This is the age requirement (see above definition).

resource. This is an instance of the currently accessed book passed into the handler.

The handler does the following:

  1. Checks the user has a data of birth claim.
  2. Checks if the library book genre is of type “Adult” (we may have a better way to define adult books in the table). If the book is not of “Adult” type then pass authorization.
  3. Checks the age of the user is adult, if so then pass authorization.

To access the above handler, the code snip below can be used in a controller to test authorization using the above claim.

// GET: LoanViewModels/Create
[HttpGet("api/[controller]/Create/{id}")]
public async Task<IActionResult> Create(int id)
{
  // use imperative authorization to check age restriction on library books.
  BookViewModel bookView = await _bookService.GetBook(id);

  if ((await _authorizationService.AuthorizeAsync(
         User, bookView, 
         new MinimumAgeRequirement(18))).Succeeded)
  {
         LoanViewModel lvm = _loanService.CreateNewBookLoan(id);
         lvm.LoanedBy = User.Identity.Name;
         BookLoan.Views.Loan.CreateModel createModel = new  
            CreateModel(_context);
         createModel.LoanViewModel = lvm;
         return View(createModel);
  }
  else
  {
     return new ChallengeResult(); 
  }
}

We have seen how to implement a claim based imperative authorization handler to apply some useful business rules to protect sections of code within methods in an ASP.NET Core application.

We used a combination of a requirements rule, the data within a library SQL data store and the claims of an incoming user to achieve our goal.

That’s all for today.

Hope this has been an informative post and you have learnt something useful.

Social media & sharing icons powered by UltimatelySocial