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<T>.
#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

