Role-based Authorization
[Authorize(Roles = "Admin")] public IActionResult AdminOnly() => Ok(); [Authorize(Roles = "Admin,Manager")] // OR public IActionResult AdminOrManager() => Ok(); // Multiple [Authorize] = AND [Authorize(Roles = "Admin")] [Authorize(Roles = "VerifiedUser")] public IActionResult Both() => Ok();
Adding Roles to JWT
var claims = new List<Claim> {
new(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
new(ClaimTypes.Role, user.Role), // Admin/User/Manager
new(ClaimTypes.Role, "VerifiedUser") // multiple roles
};
Policy-based Authorization ⭐
// Program.cs
builder.Services.AddAuthorization(opt => {
opt.AddPolicy("AdminOnly", p => p.RequireRole("Admin"));
opt.AddPolicy("Adult", p => p.RequireClaim("Age", "18", "19", "20" /* ... */));
opt.AddPolicy("Bangladeshi", p => p.RequireClaim("Country", "BD"));
opt.AddPolicy("AdminOrOwner", p =>
p.RequireAssertion(ctx =>
ctx.User.IsInRole("Admin") ||
ctx.User.HasClaim("permission", "owner")));
});
// Use
[Authorize(Policy = "AdminOnly")]
public IActionResult Admin() => Ok();
Custom Authorization Requirement
// 1. Requirement
public class MinimumAgeRequirement : IAuthorizationRequirement {
public int MinimumAge { get; }
public MinimumAgeRequirement(int age) => MinimumAge = age;
}
// 2. Handler
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement> {
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext ctx,
MinimumAgeRequirement req)
{
var dob = ctx.User.FindFirst("DateOfBirth")?.Value;
if (dob != null) {
var age = DateTime.UtcNow.Year - DateTime.Parse(dob).Year;
if (age >= req.MinimumAge) ctx.Succeed(req);
}
return Task.CompletedTask;
}
}
// 3. Register
builder.Services.AddAuthorization(opt => {
opt.AddPolicy("Adult", p => p.Requirements.Add(new MinimumAgeRequirement(18)));
});
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
Resource-based Authorization ⭐
"User শুধু নিজের post edit করতে পারবে"
public class PostEditHandler : AuthorizationHandler<OperationAuthorizationRequirement, Post> {
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext ctx,
OperationAuthorizationRequirement req,
Post post)
{
var userId = ctx.User.FindFirst(JwtRegisteredClaimNames.Sub)?.Value;
if (post.AuthorId.ToString() == userId || ctx.User.IsInRole("Admin"))
ctx.Succeed(req);
return Task.CompletedTask;
}
}
// Controller
[Authorize]
public class PostsController : ControllerBase {
private readonly IAuthorizationService _auth;
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, PostDto dto) {
var post = await _db.Posts.FindAsync(id);
if (post == null) return NotFound();
var result = await _auth.AuthorizeAsync(User, post, Operations.Edit);
if (!result.Succeeded) return Forbid();
// ... update
return Ok();
}
}
Reading Claims in Action
public IActionResult GetMe() {
int userId = int.Parse(User.FindFirst(JwtRegisteredClaimNames.Sub)!.Value);
string email = User.FindFirst(ClaimTypes.Email)!.Value;
bool isAdmin = User.IsInRole("Admin");
var allRoles = User.FindAll(ClaimTypes.Role).Select(c => c.Value).ToList();
return Ok(new { userId, email, isAdmin, allRoles });
}