liveBook · Manning

Authorization handler

As explained in ASP.NET Core in Action, 3e

AuthorizationHandler is an ASP.NET Core base class for implementing logic that evaluates authorization requirements, optionally against a resource.

An authorization handler decides whether a user satisfies a requirement in an ASP.NET Core authorization policy. The requirement describes what must be true; the handler contains the logic that checks it. Use a custom handler when a claim check alone cannot express the rule, or when the decision depends on the resource a user wants to access. section 24.4.2 section 24.5

To add a handler to a policy:

  • Define an IAuthorizationRequirement. It can be an empty marker or hold a value, such as a minimum age. Keep it lightweight: requirements are constructed directly, while handlers can use dependency injection. section 24.4.2
  • Add the requirement to a named policy in Program.cs, then implement AuthorizationHandler<TRequirement> and override HandleRequirementAsync. section 24.4.2
  • Register the handler as an IAuthorizationHandler in the dependency-injection container. Apply the policy to a Razor Page or action with [Authorize("PolicyName")]. section 24.4.2
Listing 24.8 FrequentFlyerHandler for AllowedInLoungeRequirement
public class FrequentFlyerHandler :
    AuthorizationHandler<AllowedInLoungeRequirement>              #1
{
    protected override Task HandleRequirementAsync(               #2
        AuthorizationHandlerContext context,                      #3
        AllowedInLoungeRequirement requirement)                   #4
    {
        if(context.User.HasClaim("FrequentFlyerClass", "Gold"))   #5
        {
            context.Succeed(requirement);                         #6
        }
        return Task.CompletedTask;                                #7
    }
}
#1 The handler implements AuthorizationHandler&lt;T&gt;.
#2 You must override the abstract HandleRequirementAsync method.
#3 The context contains details such as the ClaimsPrincipal user object.
#4 The requirement instance to handle
#5 Checks whether the user has the Frequent-FlyerClass claim with the Gold value
#6 If the user had the necessary claim, marks the requirement as satisfied by calling Succeed
#7 If the requirement wasn’t satisfied, does nothing

This handler checks the current user's Gold frequent-flyer claim and calls context.Succeed(requirement) when it finds a match. section 24.4.2

A handler has three choices: call context.Succeed(requirement), do nothing, or call context.Fail(). Doing nothing leaves another handler free to satisfy the requirement. An explicit failure overrides success from another handler, so reserve Fail() for a rule that must block access. Every requirement in a policy must be satisfied, but any one of its handlers can supply that success if none explicitly fails. section 24.4.2 section 24.7

Who can enter the lounge?

A lounge policy has a minimum-age requirement and an entry requirement. A Gold frequent-flyer handler and an airline-employee handler offer two ways to satisfy entry; a banned-user handler calls Fail() to deny entry despite either success. The age requirement still has to be satisfied. section 24.4 section 24.4.2

For resource-based authorization, load the protected item before checking access. A recipe-edit policy, for example, needs the recipe to determine whether the current user created it. An [Authorize] attribute cannot pass that loaded recipe to the check: call IAuthorizationService.AuthorizeAsync(user, recipe, policy) after loading it. Resource-based handlers can use AuthorizationHandler<TRequirement, TResource>; the same handlers can be reused across Razor Pages and minimal APIs. section 24.5 section 24.5.1 section 25.6

Listing 25.3 Using resource authorization to protect a minimal API endpoint
app.MapDelete("recipe/{id}", async (
    int id, RecipeService service,
    IAuthorizationService authService,                #1
    ClaimsPrincipal user) =>                          #2
{
    var recipe = await service.GetRecipe(id);         #3
    var result = await authService.AuthorizeAsync(    #4
        user, recipe, "CanManageRecipe");             #4
 
    if (!result.Succeeded)                            #5
    {                                                 #5
        return Results.Forbid();                      #5
    }                                                 #5
 
    await service.DeleteRecipe(id);                   #6
    return Results.NoContent();                       #6
});
#1 Injected to perform resource-based authorization
#2 The HttpContext.User claims principal for the request
#3 Fetches the recipe to access
#4 Performs resource-based authorization, passing in the user, resource, and the policy name
#5 If authorization failed, returns 403 Forbidden
#6 If authorization succeeded, executes the endpoint as normal

The delete endpoint loads a recipe, authorizes the user against that recipe, returns Forbidden on failure, and deletes it only after success. section 25.6

How ASP.NET Core in Action, 3e covers it

How deep it goes
Works through requirement creation, handler outcomes, policy configuration, registration, and manual resource checks; does not detail the authorization framework's internals.
Examples
Uses Gold frequent flyers, airline employees, and banned users for lounge access, then recipe ownership for edit and delete checks.
What it assumes
Builds on policies and claim checks while introducing custom requirements and handlers; resource checks also use dependency injection.
What you can do after
Build it from scratch: define and register handlers for a custom policy, or apply them to a loaded resource through IAuthorizationService.
How it connects
Connects authorization policies, IAuthorizationRequirement, claims, dependency injection, resource-based authorization, and IAuthorizationService.

Also explained in

Compare how 2 books explain authorization handler →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of ASP.NET Core in Action, 3e. The section links point to the parts of the book it draws on.