Master ASP.NET Core authorization: role-based, claims-based, policy-based, and resource-based access control.
Authorization determines what authenticated users are allowed to do. ASP.NET Core supports role-based, claims-based, and policy-based authorization, enabling fine-grained access control from simple role guards to complex resource-level permissions.
Apply [Authorize(Roles = ...)] and claims-based checks:
1// Setup roles in Program.cs
2builder.Services.AddAuthorization();
3
4// Seed roles
5public static async Task SeedRolesAsync(IServiceProvider services)
6{
7 var roleManager = services.GetRequiredService<RoleManager<IdentityRole>>();
8
9 string[] roles = { "Admin", "Manager", "User", "Premium" };
10
11 foreach (var role in roles)
12 {
13 if (!await roleManager.RoleExistsAsync(role))
14 {
15 await roleManager.CreateAsync(new IdentityRole(role));
16 }
17 }
18}
19
20// Controller with role-based authorization
21[ApiController]
22[Route("api/[controller]")]
23[Authorize] // Requires authentication
24public class AdminController : ControllerBase
25{
26 // Only Admin role can access
27 [HttpGet("dashboard")]
28 [Authorize(Roles = "Admin")]
29 public IActionResult GetDashboard()
30 {
31 return Ok(new { message = "Admin Dashboard" });
32 }
33
34 // Admin OR Manager can access
35 [HttpGet("reports")]
36 [Authorize(Roles = "Admin,Manager")]
37 public IActionResult GetReports()
38 {
39 return Ok(new { message = "Reports" });
40 }
41
42 // Multiple role attributes (AND logic)
43 [HttpPost("settings")]
44 [Authorize(Roles = "Admin")]
45 [Authorize(Roles = "Premium")]
46 public IActionResult UpdateSettings()
47 {
48 // User must be BOTH Admin AND Premium
49 return Ok(new { message = "Settings updated" });
50 }
51}
52
53// Assign roles to users
54public async Task<IActionResult> AssignRole(string userId, string role)
55{
56 var user = await _userManager.FindByIdAsync(userId);
57 if (user == null) return NotFound();
58
59 var result = await _userManager.AddToRoleAsync(user, role);
60 if (result.Succeeded)
61 {
62 return Ok(new { message = $"Role '{role}' assigned to user" });
63 }
64
65 return BadRequest(result.Errors);
66}
67
68// Check user roles
69var isAdmin = await _userManager.IsInRoleAsync(user, "Admin");
70var userRoles = await _userManager.GetRolesAsync(user);Define authorization policies with IAuthorizationRequirement and IAuthorizationHandler:
1// Add claims to user
2public async Task AddUserClaims(ApplicationUser user)
3{
4 var claims = new List<Claim>
5 {
6 new Claim("Department", "Engineering"),
7 new Claim("EmployeeId", "EMP001"),
8 new Claim("StartDate", "2023-01-15"),
9 new Claim("Permission", "CanEditProducts"),
10 new Claim("Permission", "CanViewReports"),
11 new Claim("Subscription", "Premium"),
12 new Claim("Level", "Senior")
13 };
14
15 await _userManager.AddClaimsAsync(user, claims);
16}
17
18// Authorization policies based on claims
19builder.Services.AddAuthorization(options =>
20{
21 // Simple claim check
22 options.AddPolicy("EngineeringOnly", policy =>
23 policy.RequireClaim("Department", "Engineering"));
24
25 // Multiple claim values (OR)
26 options.AddPolicy("TechTeam", policy =>
27 policy.RequireClaim("Department", "Engineering", "IT", "QA"));
28
29 // Custom claim requirement
30 options.AddPolicy("SeniorEmployee", policy =>
31 policy.RequireAssertion(context =>
32 {
33 var startDate = context.User.FindFirstValue("StartDate");
34 if (DateTime.TryParse(startDate, out var date))
35 {
36 return DateTime.Now.Subtract(date).TotalDays > 365;
37 }
38 return false;
39 }));
40
41 // Require specific permission claim
42 options.AddPolicy("CanEditProducts", policy =>
43 policy.RequireClaim("Permission", "CanEditProducts"));
44});
45
46// Use in controllers
47[HttpGet("engineering")]
48[Authorize(Policy = "EngineeringOnly")]
49public IActionResult EngineeringEndpoint()
50{
51 var dept = User.FindFirstValue("Department");
52 return Ok(new { message = $"Welcome, {dept} team member!" });
53}
54
55[HttpGet("products/edit")]
56[Authorize(Policy = "CanEditProducts")]
57public IActionResult EditProducts()
58{
59 return Ok(new { message = "You can edit products" });
60}
61
62// Access claims in controller
63[HttpGet("me/claims")]
64[Authorize]
65public IActionResult GetMyClaims()
66{
67 var claims = User.Claims.Select(c => new { c.Type, c.Value });
68 return Ok(claims);
69}
70
71// Check claim manually
72[HttpGet("check-permission")]
73[Authorize]
74public IActionResult CheckPermission(string permission)
75{
76 var hasPermission = User.HasClaim("Permission", permission);
77 return Ok(new { permission, hasPermission });
78}Perform authorization decisions based on the specific resource being accessed:
1// Custom requirement
2public class MinimumAgeRequirement : IAuthorizationRequirement
3{
4 public int MinimumAge { get; }
5 public MinimumAgeRequirement(int age) => MinimumAge = age;
6}
7
8// Custom handler
9public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
10{
11 protected override Task HandleRequirementAsync(
12 AuthorizationHandlerContext context,
13 MinimumAgeRequirement requirement)
14 {
15 var dobClaim = context.User.FindFirstValue("DateOfBirth");
16
17 if (DateTime.TryParse(dobClaim, out var dob))
18 {
19 var age = DateTime.Today.Year - dob.Year;
20 if (dob.Date > DateTime.Today.AddYears(-age)) age--;
21
22 if (age >= requirement.MinimumAge)
23 {
24 context.Succeed(requirement);
25 }
26 }
27
28 return Task.CompletedTask;
29 }
30}
31
32// Register and configure
33builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
34builder.Services.AddAuthorization(options =>
35{
36 options.AddPolicy("AtLeast18", policy =>
37 policy.Requirements.Add(new MinimumAgeRequirement(18)));
38
39 options.AddPolicy("AtLeast21", policy =>
40 policy.Requirements.Add(new MinimumAgeRequirement(21)));
41});
42
43// Complex policy with multiple requirements
44public class ContentCreatorRequirement : IAuthorizationRequirement { }
45
46public class ContentCreatorHandler : AuthorizationHandler<ContentCreatorRequirement>
47{
48 protected override Task HandleRequirementAsync(
49 AuthorizationHandlerContext context,
50 ContentCreatorRequirement requirement)
51 {
52 // Check multiple conditions
53 var hasCreatorRole = context.User.IsInRole("Creator") || context.User.IsInRole("Admin");
54 var isPremium = context.User.HasClaim("Subscription", "Premium");
55 var isVerified = context.User.HasClaim("Verified", "true");
56
57 if (hasCreatorRole && (isPremium || isVerified))
58 {
59 context.Succeed(requirement);
60 }
61
62 return Task.CompletedTask;
63 }
64}
65
66builder.Services.AddAuthorization(options =>
67{
68 options.AddPolicy("ContentCreator", policy =>
69 {
70 policy.RequireAuthenticatedUser();
71 policy.Requirements.Add(new ContentCreatorRequirement());
72 });
73});
74
75// Use policy
76[HttpPost("content")]
77[Authorize(Policy = "ContentCreator")]
78public IActionResult CreateContent([FromBody] ContentDto content)
79{
80 return Ok(new { message = "Content created" });
81}Authorization best practices to apply across your application:
1// Resource operation requirements
2public static class Operations
3{
4 public static OperationAuthorizationRequirement Create =
5 new() { Name = nameof(Create) };
6 public static OperationAuthorizationRequirement Read =
7 new() { Name = nameof(Read) };
8 public static OperationAuthorizationRequirement Update =
9 new() { Name = nameof(Update) };
10 public static OperationAuthorizationRequirement Delete =
11 new() { Name = nameof(Delete) };
12}
13
14// Resource entity
15public class Document
16{
17 public int Id { get; set; }
18 public string Title { get; set; } = "";
19 public string OwnerId { get; set; } = "";
20 public bool IsPublic { get; set; }
21 public List<string> SharedWithUserIds { get; set; } = new();
22}
23
24// Resource-based handler
25public class DocumentAuthorizationHandler :
26 AuthorizationHandler<OperationAuthorizationRequirement, Document>
27{
28 protected override Task HandleRequirementAsync(
29 AuthorizationHandlerContext context,
30 OperationAuthorizationRequirement requirement,
31 Document resource)
32 {
33 var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
34 var isAdmin = context.User.IsInRole("Admin");
35
36 // Admin can do everything
37 if (isAdmin)
38 {
39 context.Succeed(requirement);
40 return Task.CompletedTask;
41 }
42
43 // Owner can do everything with their documents
44 if (resource.OwnerId == userId)
45 {
46 context.Succeed(requirement);
47 return Task.CompletedTask;
48 }
49
50 // Check specific operations
51 switch (requirement.Name)
52 {
53 case nameof(Operations.Read):
54 if (resource.IsPublic || resource.SharedWithUserIds.Contains(userId!))
55 {
56 context.Succeed(requirement);
57 }
58 break;
59
60 case nameof(Operations.Update):
61 if (resource.SharedWithUserIds.Contains(userId!) &&
62 context.User.HasClaim("Permission", "CanEditShared"))
63 {
64 context.Succeed(requirement);
65 }
66 break;
67 }
68
69 return Task.CompletedTask;
70 }
71}
72
73// Use in controller
74[ApiController]
75[Route("api/documents")]
76public class DocumentsController : ControllerBase
77{
78 private readonly IAuthorizationService _authService;
79 private readonly IDocumentRepository _docs;
80
81 [HttpGet("{id}")]
82 public async Task<IActionResult> GetDocument(int id)
83 {
84 var document = await _docs.GetByIdAsync(id);
85 if (document == null) return NotFound();
86
87 var authResult = await _authService.AuthorizeAsync(User, document, Operations.Read);
88 if (!authResult.Succeeded) return Forbid();
89
90 return Ok(document);
91 }
92
93 [HttpPut("{id}")]
94 public async Task<IActionResult> UpdateDocument(int id, DocumentDto dto)
95 {
96 var document = await _docs.GetByIdAsync(id);
97 if (document == null) return NotFound();
98
99 var authResult = await _authService.AuthorizeAsync(User, document, Operations.Update);
100 if (!authResult.Succeeded) return Forbid();
101
102 // Update logic...
103 return Ok(document);
104 }
105}Your progress is saved in this browser, on this device only.