Claims-Based Authorization and Policies in C
This article provides a practical guide to implementing claims-based security and custom authorization policies in modern C# applications using ASP.NET Core. You will learn the fundamentals of claims, how to define simple and requirement-driven policies, how to apply them declaratively to endpoints, and how to perform imperative security checks directly within your business logic.
Understanding Claims and Identities
In .NET, an identity is represented by a ClaimsIdentity,
which contains a collection of claims. A claim is a key-value pair
issued by a trusted authority describing a specific user attribute—such
as an email address, department, or date of birth. Rather than relying
solely on traditional role checks, claims provide fine-grained context
about what an identity is and what it represents.
Configuring Basic Claims-Based Policies
Policies decouple authorization logic from controllers and endpoints.
You register authorization policies in your application's setup file
(Program.cs) using AddAuthorization.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication();
builder.Services.AddAuthorization(options =>
{
// Requires the presence of a specific claim, regardless of value
options.AddPolicy("MustBeEmployee", policy =>
policy.RequireClaim("EmployeeId"));
// Requires the claim to have one of the specified values
options.AddPolicy("ITDepartmentOnly", policy =>
policy.RequireClaim("Department", "IT", "DevOps"));
});
var app = builder.Build();Applying Policies to Endpoints
You can apply policies declaratively using the
[Authorize] attribute on controllers or Minimal API route
handlers.
Using Controllers:
[ApiController]
[Route("api/[controller]")]
public class InternalDataController : ControllerBase
{
[HttpGet]
[Authorize(Policy = "ITDepartmentOnly")]
public IActionResult GetRestrictedData()
{
return Ok(new { Data = "Sensitive IT Configuration" });
}
}Using Minimal APIs:
app.MapGet("/api/employee-portal", () => "Welcome to the intranet")
.RequireAuthorization("MustBeEmployee");Creating Custom Authorization Requirements and Handlers
When authorization rules require custom logic beyond simple string matching—such as calculating age or validating against a database—you combine custom requirements with authorization handlers.
1. Define the Requirement
A requirement implements IAuthorizationRequirement:
using Microsoft.AspNetCore.Authorization;
public class MinimumExperienceRequirement : IAuthorizationRequirement
{
public int RequiredYears { get; }
public MinimumExperienceRequirement(int requiredYears)
{
RequiredYears = requiredYears;
}
}2. Implement the Authorization Handler
The handler evaluates the claims of the current user against the requirement:
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
public class MinimumExperienceHandler : AuthorizationHandler<MinimumExperienceRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumExperienceRequirement requirement)
{
var hireDateClaim = context.User.FindFirst(c => c.Type == "HireDate");
if (hireDateClaim != null && DateTime.TryParse(hireDateClaim.Value, out var hireDate))
{
var yearsOfExperience = (DateTime.UtcNow - hireDate).TotalDays / 365.25;
if (yearsOfExperience >= requirement.RequiredYears)
{
context.Succeed(requirement);
}
}
return Task.CompletedTask;
}
}3. Register the Policy and Handler
Register the handler in the dependency injection container and add the policy to authorization options:
builder.Services.AddSingleton<IAuthorizationHandler, MinimumExperienceHandler>();
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("SeniorStaffOnly", policy =>
policy.Requirements.Add(new MinimumExperienceRequirement(5)));
});Performing Imperative Authorization Checks
When authorization depends on runtime data or resources not known
until the method executes, inject IAuthorizationService to
evaluate policies imperatively.
[ApiController]
[Route("api/documents")]
public class DocumentController : ControllerBase
{
private readonly IAuthorizationService _authorizationService;
public DocumentController(IAuthorizationService authorizationService)
{
_authorizationService = authorizationService;
}
[HttpPost("{id}/publish")]
public async Task<IActionResult> PublishDocument(int id)
{
var result = await _authorizationService.AuthorizeAsync(User, "SeniorStaffOnly");
if (!result.Succeeded)
{
return Forbid();
}
return Ok($"Document {id} published.");
}
}