Section 6 of 6

Custom Authorization Handlers

🎯 What You'll Learn

  • Creating custom requirements
  • Creating custom handlers
  • Dependency injection in handlers
  • Multiple handlers
  • Complete examples

Creating Custom Requirements

Custom requirements allow you to implement complex authorization logic beyond roles and claims.

MinimumAgeRequirement.cs C#
public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

Creating Custom Handlers

MinimumAgeHandler.cs C#
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        MinimumAgeRequirement requirement)
    {
        var birthDateClaim = context.User.FindFirst("BirthDate");
        
        if (birthDateClaim == null)
            return Task.CompletedTask;

        if (DateTime.TryParse(birthDateClaim.Value, out var birthDate))
        {
            var age = DateTime.Today.Year - birthDate.Year;
            
            if (age >= requirement.MinimumAge)
            {
                context.Succeed(requirement);
            }
        }

        return Task.CompletedTask;
    }
}

Dependency Injection in Handlers

Handler with DbContext C#
public class ActiveSubscriptionHandler 
    : AuthorizationHandler<ActiveSubscriptionRequirement>
{
    private readonly InvenTrackDbContext _context;

    public ActiveSubscriptionHandler(InvenTrackDbContext context)
    {
        _context = context;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        ActiveSubscriptionRequirement requirement)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        var hasActiveSubscription = await _context.Subscriptions
            .AnyAsync(s => s.UserId == userId && s.IsActive);

        if (hasActiveSubscription)
        {
            context.Succeed(requirement);
        }
    }
}

Registering Handlers

Program.cs C#
// Register handlers
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
builder.Services.AddScoped<IAuthorizationHandler, ActiveSubscriptionHandler>();

// Create policies
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
        policy.Requirements.Add(new MinimumAgeRequirement(18)));

    options.AddPolicy("ActiveSubscription", policy =>
        policy.Requirements.Add(new ActiveSubscriptionRequirement()));
});

Complete InvenTrack Example

BusinessHoursRequirement.cs C#
public class BusinessHoursRequirement : IAuthorizationRequirement
{
}
BusinessHoursHandler.cs C#
public class BusinessHoursHandler : AuthorizationHandler<BusinessHoursRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        BusinessHoursRequirement requirement)
    {
        var currentHour = DateTime.Now.Hour;

        // Business hours: 9 AM - 5 PM
        if (currentHour >= 9 && currentHour < 17)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

// Usage in Program.cs
builder.Services.AddSingleton<IAuthorizationHandler, BusinessHoursHandler>();

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("BusinessHoursOnly", policy =>
        policy.Requirements.Add(new BusinessHoursRequirement()));
});

// Usage in Controller
[Authorize(Policy = "BusinessHoursOnly")]
public IActionResult CreateOrder()
{
    return View();
}

Key Takeaways

  • IAuthorizationRequirement: Define custom requirement
  • AuthorizationHandler: Implement authorization logic
  • context.Succeed(): Mark requirement as satisfied
  • Dependency injection: Inject services into handlers
  • Singleton vs Scoped: Choose based on dependencies
  • Flexible: Implement any authorization logic
🎉 Part XIII Complete!

Congratulations! You've completed Part XIII: Authorization. You now understand:

  • ✅ Authorization fundamentals ([Authorize], UseAuthorization)
  • ✅ Role-based authorization (roles, [Authorize(Roles)])
  • ✅ Claims-based authorization (claims, RequireClaim)
  • ✅ Policy-based authorization (policies, combining requirements)
  • ✅ Resource-based authorization (IAuthorizationService, AuthorizeAsync)
  • ✅ Custom authorization handlers (IAuthorizationRequirement, AuthorizationHandler)

You now have the skills to implement comprehensive authorization in your ASP.NET Core applications! 🚀