Policy-Based Authorization
Policy-based authorization in ASP.NET Core evaluates requirements against the current user's claims. Unlike simple role checks, policies can combine multiple conditions, perform async database lookups, and express complex business rules ('user must own this resource AND have a verified email AND be in a Pro plan').
IAuthorizationRequirement & PolicyBuilder
using Microsoft.AspNetCore.Authorization;
// ━━ Custom requirement ━━
// 'User must have verified their phone number'
public class PhoneVerifiedRequirement : IAuthorizationRequirement { }
// ━━ Handler evaluates the requirement ━━
public class PhoneVerifiedHandler : AuthorizationHandler<PhoneVerifiedRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PhoneVerifiedRequirement requirement)
{
if (context.User.HasClaim("phone_verified", "true"))
context.Succeed(requirement);
else
context.Fail(new AuthorizationFailureReason(this, "Phone number not verified"));
return Task.CompletedTask;
}
}
// ━━ Combined requirement — subscription tier ━━
public class MinimumSubscriptionRequirement(string minimumTier) : IAuthorizationRequirement
{
public string MinimumTier { get; } = minimumTier;
}
public class SubscriptionTierHandler : AuthorizationHandler<MinimumSubscriptionRequirement>
{
private static readonly Dictionary<string, int> TierRank = new()
{ ["free"] = 0, ["starter"] = 1, ["pro"] = 2, ["enterprise"] = 3 };
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumSubscriptionRequirement requirement)
{
var userTier = context.User.FindFirstValue("subscription_tier") ?? "free";
var required = requirement.MinimumTier;
if (TierRank.TryGetValue(userTier, out var userRank) &&
TierRank.TryGetValue(required, out var reqRank) &&
userRank >= reqRank)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
// ━━ Register policies ━━
builder.Services.AddAuthorizationBuilder()
.AddPolicy("RequireAdmin", policy => policy.RequireRole("Admin"))
.AddPolicy("RequireManager", policy => policy.RequireRole("Admin", "Manager"))
.AddPolicy("RequireVerifiedPhone", policy =>
policy.RequireAuthenticatedUser()
.AddRequirements(new PhoneVerifiedRequirement()))
.AddPolicy("RequireProPlan", policy =>
policy.RequireAuthenticatedUser()
.AddRequirements(new MinimumSubscriptionRequirement("pro")))
.AddPolicy("CanManageProducts", policy =>
policy.RequireAuthenticatedUser()
.RequireRole("Admin", "Manager")
.RequireClaim("department", "catalog"))
.AddPolicy("SameCountryOnly", policy =>
policy.RequireClaim("country", "US", "CA", "GB")); // enumerable overload
builder.Services.AddSingleton<IAuthorizationHandler, PhoneVerifiedHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, SubscriptionTierHandler>();
// ━━ Usage ━━
[Authorize(Policy = "RequireProPlan")]
[HttpGet("analytics")]
public IActionResult GetAnalytics() => Ok("Pro analytics data");Tip
Tip
Practice PolicyBased Authorization in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
.NET provides a unified platform for building various application types
Practice Task
Note
Practice Task — (1) Write a working example of PolicyBased Authorization from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with PolicyBased Authorization is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready dotnet code.