What is policy-based authorization, and how does it compare to role-based?
Quick Answer
Role-based ([Authorize(Roles = "Admin")]) checks a single claim value. Policy-based authorization names a rule ([Authorize(Policy = "CanApproveInvoices")]) that's defined once in AddAuthorization as one or more requirements evaluated by handlers — so it can combine claims, roles, ages, or any custom logic, and it's unit-testable.
Role-based authorization is a simple check that the user has a particular role claim. Policy-based is more flexible: I define a named policy in AddAuthorization as a set of requirements, and each requirement has a handler that runs whatever logic it needs — check a claim, an age, combine several conditions. The action just says [Authorize(Policy = "CanApproveInvoices")]. The advantages are that the rule lives in one place, it can be as complex as needed, and the handler is testable. When the decision depends on the specific record, I use resource-based authorization via IAuthorizationService inside the action.
- Register:
options.AddPolicy("AtLeast21", p => p.Requirements.Add(new MinimumAgeRequirement(21))). - Handler:
class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>—context.Succeed(requirement)when the rule passes. - Resource-based: when the decision depends on the specific entity (“can this user edit this document”), use
IAuthorizationService.AuthorizeAsync(User, document, "EditPolicy")inside the action. - Roles are really just a claim; policies are the flexible, maintainable model for anything non-trivial.