ASP.NET Core  

ASP.NET Core Advanced Authorization: Policy-Based Security & Resource Protection Guide (Part-14 of 40)

 

ASP.NET Core Advanced Authorization: Policy-Based Security & Resource Protection Guide (Part-14 of 40)


Previous Article: ASP.NET Core Identity Unleashed: Complete Guide to Registration, Roles, 2FA & Security

Master ASP.NET Core advanced authorization with policy-based security, resource protection, custom requirements, and real-world implementation examples for robust application security.

Table of Contents

  1. Introduction to Advanced Authorization

  2. Authorization vs Authentication

  3. Policy-Based Authorization Fundamentals

  4. Creating Custom Authorization Policies

  5. Resource-Based Authorization

  6. Custom Authorization Requirements & Handlers

  7. Role-Based Authorization Deep Dive

  8. Claims-Based Authorization

  9. Permission-Based Authorization

  10. Authorization in Razor Pages

  11. Authorization in Web APIs

  12. Advanced Scenarios & Patterns

  13. Security Best Practices

  14. Performance Considerations

  15. Testing Authorization

  16. Troubleshooting & Debugging

  17. Real-World Implementation Example

  18. Conclusion

1. Introduction to Advanced Authorization

Authorization is the process that determines what a user is allowed to do once they're authenticated. While basic authorization checks if a user is logged in, advanced authorization provides granular control over resources and operations.

Why Advanced Authorization Matters

In modern applications, simple role checks are insufficient. Consider these real-world scenarios:

  • Healthcare System: Doctors can view patient records, but only for their own patients

  • Banking Application: Users can transfer money, but only from their own accounts

  • E-commerce Platform: Sellers can edit product information, but only for their own products

  • Project Management: Team members can update tasks, but only those assigned to them

These scenarios require more sophisticated authorization than simple role-based checks.

2. Authorization vs Authentication

Authentication: Who Are You?

// Authentication confirms user identity
public async Task<IActionResult> Login(LoginModel model)
{
    var result = await _signInManager.PasswordSignInAsync(
        model.Email, model.Password, model.RememberMe, false);
    
    if (result.Succeeded)
    {
        // User is authenticated
        return RedirectToAction("Dashboard");
    }
}

Authorization: What Can You Do?

// Authorization determines permissions
[Authorize(Roles = "Admin")]
public IActionResult ManageUsers()
{
    // Only authenticated users in Admin role can access
    return View();
}

3. Policy-Based Authorization Fundamentals

Policy-based authorization is the cornerstone of advanced security in ASP.NET Core. It provides a flexible, declarative way to define authorization rules.

Basic Policy Configuration

// Program.cs or Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        // Requirement: User must be authenticated
        options.AddPolicy("Authenticated", policy =>
            policy.RequireAuthenticatedUser());

        // Requirement: User must be in Admin role
        options.AddPolicy("AdminOnly", policy =>
            policy.RequireRole("Admin"));

        // Requirement: User must have specific claim
        options.AddPolicy("Over18Only", policy =>
            policy.RequireClaim("Age", "18", "19", "20")); // Values 18+

        // Combined requirements
        options.AddPolicy("SeniorEditor", policy =>
        {
            policy.RequireAuthenticatedUser();
            policy.RequireRole("Editor");
            policy.RequireClaim("ExperienceLevel", "Senior");
        });
    });
}

Using Policies in Controllers

[Authorize(Policy = "AdminOnly")]
public class AdminController : Controller
{
    public IActionResult Dashboard() => View();
}

[Authorize(Policy = "Over18Only")]
public class AdultContentController : Controller
{
    public IActionResult RestrictedContent() => View();
}

4. Creating Custom Authorization Policies

Complex Policy with Multiple Requirements

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("PremiumUser", policy =>
        {
            policy.RequireAuthenticatedUser();
            policy.RequireClaim("SubscriptionType", "Premium", "Enterprise");
            policy.RequireClaim("AccountStatus", "Active");
            policy.Requirements.Add(new MinimumSubscriptionDurationRequirement(30)); // 30 days
        });
    });
}

Policy with Custom Logic

public class MinimumSubscriptionDurationRequirement : IAuthorizationRequirement
{
    public int MinimumDays { get; }

    public MinimumSubscriptionDurationRequirement(int minimumDays)
    {
        MinimumDays = minimumDays;
    }
}

public class MinimumSubscriptionDurationHandler 
    : AuthorizationHandler<MinimumSubscriptionDurationRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        MinimumSubscriptionDurationRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == "SubscriptionStartDate"))
        {
            return Task.CompletedTask;
        }

        var subscriptionStartDate = DateTime.Parse(
            context.User.FindFirst(c => c.Type == "SubscriptionStartDate").Value);
        
        var subscriptionDuration = DateTime.Now - subscriptionStartDate;
        
        if (subscriptionDuration.Days >= requirement.MinimumDays)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

// Registration
services.AddScoped<IAuthorizationHandler, MinimumSubscriptionDurationHandler>();

5. Resource-Based Authorization

Resource-based authorization evaluates permissions based on both the user and the specific resource being accessed.

Real-World Example: Document Management System

// Document entity
public class Document
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public string AuthorId { get; set; } // User who created the document
    public DocumentStatus Status { get; set; }
    public DateTime CreatedDate { get; set; }
}

public enum DocumentStatus
{
    Draft,
    Published,
    Archived
}

Resource Authorization Requirement

public class DocumentAuthorizationRequirement : IAuthorizationRequirement
{
    public string Operation { get; }

    public DocumentAuthorizationRequirement(string operation)
    {
        Operation = operation;
    }
}

public class DocumentAuthorizationHandler 
    : AuthorizationHandler<DocumentAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        DocumentAuthorizationRequirement requirement,
        Document resource)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        switch (requirement.Operation)
        {
            case "Read":
                if (resource.Status == DocumentStatus.Published || 
                    resource.AuthorId == userId ||
                    context.User.IsInRole("Admin"))
                {
                    context.Succeed(requirement);
                }
                break;

            case "Edit":
                if (resource.AuthorId == userId && 
                    resource.Status != DocumentStatus.Archived ||
                    context.User.IsInRole("Admin"))
                {
                    context.Succeed(requirement);
                }
                break;

            case "Delete":
                if (resource.AuthorId == userId ||
                    context.User.IsInRole("Admin"))
                {
                    context.Succeed(requirement);
                }
                break;

            case "Publish":
                if ((resource.AuthorId == userId && 
                     context.User.HasClaim("CanPublish", "true")) ||
                    context.User.IsInRole("Admin"))
                {
                    context.Succeed(requirement);
                }
                break;
        }

        return Task.CompletedTask;
    }
}

Using Resource Authorization in Controllers

public class DocumentsController : Controller
{
    private readonly DocumentService _documentService;
    private readonly IAuthorizationService _authorizationService;

    public DocumentsController(DocumentService documentService, 
        IAuthorizationService authorizationService)
    {
        _documentService = documentService;
        _authorizationService = authorizationService;
    }

    [HttpGet]
    public async Task<IActionResult> Edit(int id)
    {
        var document = await _documentService.GetDocumentAsync(id);
        
        if (document == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, document, new DocumentAuthorizationRequirement("Edit"));

        if (!authorizationResult.Succeeded)
        {
            return Forbid();
        }

        return View(document);
    }

    [HttpPost]
    public async Task<IActionResult> Delete(int id)
    {
        var document = await _documentService.GetDocumentAsync(id);
        
        if (document == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, document, new DocumentAuthorizationRequirement("Delete"));

        if (!authorizationResult.Succeeded)
        {
            return Forbid();
        }

        await _documentService.DeleteDocumentAsync(id);
        return RedirectToAction("Index");
    }
}

6. Custom Authorization Requirements & Handlers

Complex Business Rule: Department-Based Authorization

// Requirement: User must be in the same department as the resource
public class SameDepartmentRequirement : IAuthorizationRequirement
{
}

public class SameDepartmentHandler : AuthorizationHandler<SameDepartmentRequirement, DepartmentResource>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        SameDepartmentRequirement requirement,
        DepartmentResource resource)
    {
        var userDepartment = context.User.FindFirst("Department")?.Value;
        
        if (userDepartment != null && 
            userDepartment.Equals(resource.Department, StringComparison.OrdinalIgnoreCase))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Time-Based Authorization

// Requirement: Access only during business hours
public class BusinessHoursRequirement : IAuthorizationRequirement
{
    public TimeSpan StartTime { get; }
    public TimeSpan EndTime { get; }

    public BusinessHoursRequirement(TimeSpan startTime, TimeSpan endTime)
    {
        StartTime = startTime;
        EndTime = endTime;
    }
}

public class BusinessHoursHandler : AuthorizationHandler<BusinessHoursRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        BusinessHoursRequirement requirement)
    {
        var currentTime = DateTime.Now.TimeOfDay;
        
        if (currentTime >= requirement.StartTime && 
            currentTime <= requirement.EndTime)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail(new AuthorizationFailureReason(
                this, "Access allowed only during business hours"));
        }

        return Task.CompletedTask;
    }
}

Geographic Location Authorization

// Requirement: User must be accessing from specific countries
public class GeographicRequirement : IAuthorizationRequirement
{
    public string[] AllowedCountries { get; }

    public GeographicRequirement(params string[] allowedCountries)
    {
        AllowedCountries = allowedCountries;
    }
}

public class GeographicHandler : AuthorizationHandler<GeographicRequirement>
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IGeoLocationService _geoLocationService;

    public GeographicHandler(IHttpContextAccessor httpContextAccessor, 
        IGeoLocationService geoLocationService)
    {
        _httpContextAccessor = httpContextAccessor;
        _geoLocationService = geoLocationService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        GeographicRequirement requirement)
    {
        var httpContext = _httpContextAccessor.HttpContext;
        var ipAddress = httpContext.Connection.RemoteIpAddress?.ToString();

        if (string.IsNullOrEmpty(ipAddress))
        {
            context.Fail();
            return;
        }

        var country = await _geoLocationService.GetCountryFromIpAsync(ipAddress);
        
        if (requirement.AllowedCountries.Contains(country, StringComparer.OrdinalIgnoreCase))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail(new AuthorizationFailureReason(
                this, $"Access not allowed from {country}"));
        }
    }
}

7. Role-Based Authorization Deep Dive

Advanced Role Management

// Custom role service for complex role hierarchies
public interface IRoleService
{
    Task<bool> UserIsInRoleAsync(string userId, string role);
    Task<IEnumerable<string>> GetUserRolesAsync(string userId);
    Task<bool> UserHasPermissionAsync(string userId, string permission);
}

public class RoleService : IRoleService
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly RoleManager<IdentityRole> _roleManager;

    public RoleService(UserManager<ApplicationUser> userManager, 
        RoleManager<IdentityRole> roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }

    public async Task<bool> UserIsInRoleAsync(string userId, string role)
    {
        var user = await _userManager.FindByIdAsync(userId);
        return await _userManager.IsInRoleAsync(user, role);
    }

    public async Task<IEnumerable<string>> GetUserRolesAsync(string userId)
    {
        var user = await _userManager.FindByIdAsync(userId);
        return await _userManager.GetRolesAsync(user);
    }

    public async Task<bool> UserHasPermissionAsync(string userId, string permission)
    {
        var userRoles = await GetUserRolesAsync(userId);
        
        foreach (var roleName in userRoles)
        {
            var role = await _roleManager.FindByNameAsync(roleName);
            var claims = await _roleManager.GetClaimsAsync(role);
            
            if (claims.Any(c => c.Type == "Permission" && c.Value == permission))
            {
                return true;
            }
        }
        
        return false;
    }
}

Hierarchical Role System

// Custom requirement for hierarchical roles
public class MinimumRoleRequirement : IAuthorizationRequirement
{
    public string MinimumRole { get; }
    private static readonly Dictionary<string, int> RoleHierarchy = new()
    {
        ["User"] = 1,
        ["Moderator"] = 2,
        ["Admin"] = 3,
        ["SuperAdmin"] = 4
    };

    public MinimumRoleRequirement(string minimumRole)
    {
        MinimumRole = minimumRole;
    }

    public bool IsRoleSufficient(string userRole)
    {
        if (!RoleHierarchy.ContainsKey(userRole) || 
            !RoleHierarchy.ContainsKey(MinimumRole))
            return false;

        return RoleHierarchy[userRole] >= RoleHierarchy[MinimumRole];
    }
}

public class MinimumRoleHandler : AuthorizationHandler<MinimumRoleRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        MinimumRoleRequirement requirement)
    {
        var userRoles = context.User.Claims
            .Where(c => c.Type == ClaimTypes.Role)
            .Select(c => c.Value);

        foreach (var role in userRoles)
        {
            if (requirement.IsRoleSufficient(role))
            {
                context.Succeed(requirement);
                return Task.CompletedTask;
            }
        }

        context.Fail();
        return Task.CompletedTask;
    }
}

8. Claims-Based Authorization

Advanced Claims Transformation

// Custom claims transformer
public class AdvancedClaimsTransformer : IClaimsTransformation
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IPermissionService _permissionService;

    public AdvancedClaimsTransformer(UserManager<ApplicationUser> userManager,
        IPermissionService permissionService)
    {
        _userManager = userManager;
        _permissionService = permissionService;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = principal.Identity as ClaimsIdentity;
        
        if (identity == null || !identity.IsAuthenticated)
            return principal;

        // Add additional claims based on business logic
        var userId = identity.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        if (string.IsNullOrEmpty(userId))
            return principal;

        var user = await _userManager.FindByIdAsync(userId);
        
        if (user == null)
            return principal;

        // Add subscription claims
        if (user.SubscriptionExpiryDate > DateTime.UtcNow)
        {
            identity.AddClaim(new Claim("Subscription", "Active"));
            identity.AddClaim(new Claim("SubscriptionType", user.SubscriptionType));
        }

        // Add permission claims
        var permissions = await _permissionService.GetUserPermissionsAsync(userId);
        foreach (var permission in permissions)
        {
            identity.AddClaim(new Claim("Permission", permission));
        }

        // Add department claims
        if (!string.IsNullOrEmpty(user.Department))
        {
            identity.AddClaim(new Claim("Department", user.Department));
        }

        return principal;
    }
}

Dynamic Claims Evaluation

// Custom requirement for dynamic claims
public class DynamicClaimRequirement : IAuthorizationRequirement
{
    public string ClaimType { get; }
    public Func<string, bool> ValueEvaluator { get; }

    public DynamicClaimRequirement(string claimType, Func<string, bool> valueEvaluator)
    {
        ClaimType = claimType;
        ValueEvaluator = valueEvaluator;
    }
}

public class DynamicClaimHandler : AuthorizationHandler<DynamicClaimRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        DynamicClaimRequirement requirement)
    {
        var claims = context.User.FindAll(requirement.ClaimType);
        
        foreach (var claim in claims)
        {
            if (requirement.ValueEvaluator(claim.Value))
            {
                context.Succeed(requirement);
                return Task.CompletedTask;
            }
        }

        context.Fail();
        return Task.CompletedTask;
    }
}

9. Permission-Based Authorization

Permission Service Implementation

public interface IPermissionService
{
    Task<bool> HasPermissionAsync(string userId, string permission);
    Task<IEnumerable<string>> GetUserPermissionsAsync(string userId);
    Task AssignPermissionAsync(string userId, string permission);
    Task RemovePermissionAsync(string userId, string permission);
}

public class PermissionService : IPermissionService
{
    private readonly ApplicationDbContext _context;

    public PermissionService(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<bool> HasPermissionAsync(string userId, string permission)
    {
        var userPermissions = await GetUserPermissionsAsync(userId);
        return userPermissions.Contains(permission, StringComparer.OrdinalIgnoreCase);
    }

    public async Task<IEnumerable<string>> GetUserPermissionsAsync(string userId)
    {
        // Get direct user permissions
        var directPermissions = await _context.UserPermissions
            .Where(up => up.UserId == userId && up.IsActive)
            .Select(up => up.Permission.Name)
            .ToListAsync();

        // Get role-based permissions
        var rolePermissions = await _context.UserRoles
            .Where(ur => ur.UserId == userId)
            .Join(_context.RolePermissions,
                ur => ur.RoleId,
                rp => rp.RoleId,
                (ur, rp) => rp.Permission.Name)
            .ToListAsync();

        return directPermissions.Union(rolePermissions).Distinct();
    }

    public async Task AssignPermissionAsync(string userId, string permission)
    {
        var permissionEntity = await _context.Permissions
            .FirstOrDefaultAsync(p => p.Name == permission);

        if (permissionEntity == null)
        {
            permissionEntity = new Permission { Name = permission };
            _context.Permissions.Add(permissionEntity);
        }

        var userPermission = new UserPermission
        {
            UserId = userId,
            PermissionId = permissionEntity.Id,
            IsActive = true,
            GrantedDate = DateTime.UtcNow
        };

        _context.UserPermissions.Add(userPermission);
        await _context.SaveChangesAsync();
    }

    public async Task RemovePermissionAsync(string userId, string permission)
    {
        var userPermission = await _context.UserPermissions
            .Include(up => up.Permission)
            .FirstOrDefaultAsync(up => up.UserId == userId && up.Permission.Name == permission);

        if (userPermission != null)
        {
            userPermission.IsActive = false;
            userPermission.RevokedDate = DateTime.UtcNow;
            await _context.SaveChangesAsync();
        }
    }
}

Permission-Based Authorization Handler

public class PermissionRequirement : IAuthorizationRequirement
{
    public string Permission { get; }

    public PermissionRequirement(string permission)
    {
        Permission = permission;
    }
}

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly IPermissionService _permissionService;

    public PermissionHandler(IPermissionService permissionService)
    {
        _permissionService = permissionService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        PermissionRequirement requirement)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        if (string.IsNullOrEmpty(userId))
        {
            context.Fail();
            return;
        }

        if (await _permissionService.HasPermissionAsync(userId, requirement.Permission))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }
    }
}

10. Authorization in Razor Pages

Page Model Authorization

// Using conventions in Program.cs
builder.Services.AddRazorPages(options =>
{
    options.Conventions.AuthorizePage("/Contact");
    options.Conventions.AuthorizePage("/Privacy", "AdminOnly");
    options.Conventions.AuthorizeFolder("/Products");
    options.Conventions.AllowAnonymousToPage("/Products/Public");
    options.Conventions.AuthorizeAreaFolder("Admin", "/", "AdminOnly");
});

// Page model with authorization
[Authorize(Policy = "EditProduct")]
public class EditProductModel : PageModel
{
    private readonly ProductService _productService;
    private readonly IAuthorizationService _authorizationService;

    public EditProductModel(ProductService productService,
        IAuthorizationService authorizationService)
    {
        _productService = productService;
        _authorizationService = authorizationService;
    }

    [BindProperty]
    public Product Product { get; set; }

    public async Task<IActionResult> OnGetAsync(int id)
    {
        Product = await _productService.GetProductAsync(id);
        
        if (Product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, Product, new ProductAuthorizationRequirement("Edit"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        return Page();
    }

    public async Task<IActionResult> OnPostAsync()
    {
        if (!ModelState.IsValid)
            return Page();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, Product, new ProductAuthorizationRequirement("Edit"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.UpdateProductAsync(Product);
        return RedirectToPage("./Details", new { id = Product.Id });
    }
}

Razor Page Authorization in Views

@page
@model Products.IndexModel
@using Microsoft.AspNetCore.Authorization
@inject IAuthorizationService AuthorizationService

<div class="container">
    <h1>Products</h1>
    
    @if ((await AuthorizationService.AuthorizeAsync(User, "CreateProduct")).Succeeded)
    {
        <a asp-page="Create" class="btn btn-primary">Create New Product</a>
    }
    
    <div class="row">
        @foreach (var product in Model.Products)
        {
            <div class="col-md-4">
                <div class="card">
                    <div class="card-body">
                        <h5 class="card-title">@product.Name</h5>
                        <p class="card-text">@product.Description</p>
                        <p class="card-text">[email protected]</p>
                        
                        @if ((await AuthorizationService.AuthorizeAsync(User, product, "Edit")).Succeeded)
                        {
                            <a asp-page="Edit" asp-route-id="@product.Id" class="btn btn-warning">Edit</a>
                        }
                        
                        @if ((await AuthorizationService.AuthorizeAsync(User, product, "Delete")).Succeeded)
                        {
                            <form method="post" asp-page-handler="Delete" asp-route-id="@product.Id" style="display: inline;">
                                <button type="submit" class="btn btn-danger">Delete</button>
                            </form>
                        }
                    </div>
                </div>
            </div>
        }
    </div>
</div>

11. Authorization in Web APIs

API Controller Authorization

[ApiController]
[Route("api/[controller]")]
[Authorize] // All endpoints require authentication
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    private readonly IAuthorizationService _authorizationService;

    public ProductsController(IProductService productService,
        IAuthorizationService authorizationService)
    {
        _productService = productService;
        _authorizationService = authorizationService;
    }

    [HttpGet]
    [AllowAnonymous] // This endpoint is publicly accessible
    public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts()
    {
        var products = await _productService.GetAllProductsAsync();
        return Ok(products);
    }

    [HttpGet("{id}")]
    [Authorize(Policy = "ViewProduct")]
    public async Task<ActionResult<ProductDto>> GetProduct(int id)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        return Ok(product);
    }

    [HttpPost]
    [Authorize(Policy = "CreateProduct")]
    public async Task<ActionResult<ProductDto>> CreateProduct(CreateProductDto createDto)
    {
        var product = await _productService.CreateProductAsync(createDto);
        return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(int id, UpdateProductDto updateDto)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, product, new ProductAuthorizationRequirement("Edit"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.UpdateProductAsync(id, updateDto);
        return NoContent();
    }

    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, product, new ProductAuthorizationRequirement("Delete"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.DeleteProductAsync(id);
        return NoContent();
    }
}

Custom Authorization Filters for APIs

public class PermissionAuthorizationFilter : IAuthorizationFilter
{
    private readonly string _permission;
    private readonly IPermissionService _permissionService;

    public PermissionAuthorizationFilter(string permission, IPermissionService permissionService)
    {
        _permission = permission;
        _permissionService = permissionService;
    }

    public async void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        
        if (!user.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult();
            return;
        }

        var userId = user.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        if (string.IsNullOrEmpty(userId))
        {
            context.Result = new ForbidResult();
            return;
        }

        var hasPermission = await _permissionService.HasPermissionAsync(userId, _permission);
        
        if (!hasPermission)
        {
            context.Result = new ForbidResult();
        }
    }
}

public class PermissionAttribute : TypeFilterAttribute
{
    public PermissionAttribute(string permission) 
        : base(typeof(PermissionAuthorizationFilter))
    {
        Arguments = new object[] { permission };
    }
}

// Usage in API controllers
[ApiController]
[Route("api/[controller]")]
public class AdminController : ControllerBase
{
    [HttpGet("reports")]
    [Permission("ViewReports")]
    public IActionResult GetReports()
    {
        // Only users with "ViewReports" permission can access
        return Ok();
    }

    [HttpPost("users")]
    [Permission("ManageUsers")]
    public IActionResult CreateUser()
    {
        // Only users with "ManageUsers" permission can access
        return Ok();
    }
}

12. Advanced Scenarios & Patterns

Multi-Tenant Authorization

// Tenant-based authorization requirement
public class TenantRequirement : IAuthorizationRequirement
{
    public string RequiredTenant { get; }

    public TenantRequirement(string requiredTenant)
    {
        RequiredTenant = requiredTenant;
    }
}

public class TenantHandler : AuthorizationHandler<TenantRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        TenantRequirement requirement)
    {
        var userTenant = context.User.FindFirst("TenantId")?.Value;
        
        if (userTenant != null && userTenant == requirement.RequiredTenant)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail();
        }

        return Task.CompletedTask;
    }
}

// Multi-tenant resource authorization
public class MultiTenantResourceHandler 
    : AuthorizationHandler<IAuthorizationRequirement, ITenantResource>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        IAuthorizationRequirement requirement,
        ITenantResource resource)
    {
        var userTenantId = context.User.FindFirst("TenantId")?.Value;
        var resourceTenantId = resource.TenantId;

        if (userTenantId == resourceTenantId)
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail(new AuthorizationFailureReason(this, 
                "Access denied: Resource belongs to different tenant"));
        }

        return Task.CompletedTask;
    }
}

Feature Flag Authorization

// Feature-based authorization
public class FeatureRequirement : IAuthorizationRequirement
{
    public string FeatureName { get; }

    public FeatureRequirement(string featureName)
    {
        FeatureName = featureName;
    }
}

public class FeatureHandler : AuthorizationHandler<FeatureRequirement>
{
    private readonly IFeatureService _featureService;

    public FeatureHandler(IFeatureService featureService)
    {
        _featureService = featureService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        FeatureRequirement requirement)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        
        if (await _featureService.IsFeatureEnabledAsync(requirement.FeatureName, userId))
        {
            context.Succeed(requirement);
        }
        else
        {
            context.Fail(new AuthorizationFailureReason(this, 
                $"Feature '{requirement.FeatureName}' is not enabled"));
        }
    }
}

Composite Authorization

// Composite requirement that combines multiple requirements
public class CompositeRequirement : IAuthorizationRequirement
{
    public IAuthorizationRequirement[] Requirements { get; }

    public CompositeRequirement(params IAuthorizationRequirement[] requirements)
    {
        Requirements = requirements;
    }
}

public class CompositeHandler : AuthorizationHandler<CompositeRequirement>
{
    private readonly IAuthorizationService _authorizationService;

    public CompositeHandler(IAuthorizationService authorizationService)
    {
        _authorizationService = authorizationService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        CompositeRequirement requirement)
    {
        foreach (var subRequirement in requirement.Requirements)
        {
            var result = await _authorizationService.AuthorizeAsync(
                context.User, context.Resource, subRequirement);

            if (!result.Succeeded)
            {
                context.Fail();
                return;
            }
        }

        context.Succeed(requirement);
    }
}

13. Security Best Practices

Principle of Least Privilege

// Grant minimum required permissions
public class LeastPrivilegeService
{
    public async Task AssignDefaultPermissionsAsync(string userId, string userType)
    {
        var permissions = GetDefaultPermissions(userType);
        
        foreach (var permission in permissions)
        {
            await _permissionService.AssignPermissionAsync(userId, permission);
        }
    }

    private IEnumerable<string> GetDefaultPermissions(string userType)
    {
        return userType switch
        {
            "Viewer" => new[] { "ReadContent", "ViewReports" },
            "Editor" => new[] { "ReadContent", "EditContent", "ViewReports" },
            "Admin" => new[] { "ReadContent", "EditContent", "DeleteContent", 
                              "ViewReports", "ManageUsers" },
            _ => new[] { "ReadContent" }
        };
    }
}

Secure Default Configuration

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        // Default policy: deny all by default
        options.DefaultPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();

        // Fallback policy for unprotected endpoints
        options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();

        // Specific policies with clear naming
        options.AddPolicy("Content.Read", policy =>
            policy.RequireAuthenticatedUser()
                  .RequireClaim("Permission", "ReadContent"));

        options.AddPolicy("Content.Write", policy =>
            policy.RequireAuthenticatedUser()
                  .RequireClaim("Permission", "WriteContent")
                  .RequireAssertion(context =>
                      context.User.HasClaim("Status", "Active")));
    });
}

Audit Logging for Authorization

public class AuditingAuthorizationHandler : IAuthorizationHandler
{
    private readonly ILogger<AuditingAuthorizationHandler> _logger;
    private readonly IAuthorizationHandler _innerHandler;

    public AuditingAuthorizationHandler(ILogger<AuditingAuthorizationHandler> logger,
        IAuthorizationHandler innerHandler)
    {
        _logger = logger;
        _innerHandler = innerHandler;
    }

    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
        var requirements = context.Requirements.ToList();

        await _innerHandler.HandleAsync(context);

        foreach (var requirement in requirements)
        {
            var succeeded = context.HasSucceeded(requirement);
            
            _logger.LogInformation(
                "Authorization audit - User: {UserId}, Requirement: {Requirement}, Result: {Result}",
                userId, requirement.GetType().Name, succeeded ? "Granted" : "Denied");
        }
    }
}

14. Performance Considerations

Caching Authorization Results

public class CachingAuthorizationService : IAuthorizationService
{
    private readonly IAuthorizationService _innerService;
    private readonly IMemoryCache _cache;
    private readonly TimeSpan _cacheDuration = TimeSpan.FromMinutes(5);

    public CachingAuthorizationService(IAuthorizationService innerService, 
        IMemoryCache cache)
    {
        _innerService = innerService;
        _cache = cache;
    }

    public async Task<AuthorizationResult> AuthorizeAsync(
        ClaimsPrincipal user, object resource, string policyName)
    {
        var cacheKey = $"auth_{user.Identity.Name}_{policyName}_{resource?.GetHashCode()}";
        
        if (_cache.TryGetValue(cacheKey, out AuthorizationResult cachedResult))
        {
            return cachedResult;
        }

        var result = await _innerService.AuthorizeAsync(user, resource, policyName);
        
        _cache.Set(cacheKey, result, _cacheDuration);
        
        return result;
    }

    public async Task<AuthorizationResult> AuthorizeAsync(
        ClaimsPrincipal user, object resource, 
        IEnumerable<IAuthorizationRequirement> requirements)
    {
        // Similar caching implementation for requirements-based authorization
        var requirementsHash = string.Join("_", requirements.Select(r => r.GetType().Name));
        var cacheKey = $"auth_req_{user.Identity.Name}_{requirementsHash}_{resource?.GetHashCode()}";
        
        if (_cache.TryGetValue(cacheKey, out AuthorizationResult cachedResult))
        {
            return cachedResult;
        }

        var result = await _innerService.AuthorizeAsync(user, resource, requirements);
        
        _cache.Set(cacheKey, result, _cacheDuration);
        
        return result;
    }
}

Optimized Permission Loading

public class OptimizedPermissionService : IPermissionService
{
    private readonly ApplicationDbContext _context;
    private readonly IMemoryCache _cache;

    public OptimizedPermissionService(ApplicationDbContext context, IMemoryCache cache)
    {
        _context = context;
        _cache = cache;
    }

    public async Task<IEnumerable<string>> GetUserPermissionsAsync(string userId)
    {
        var cacheKey = $"user_permissions_{userId}";
        
        if (_cache.TryGetValue(cacheKey, out IEnumerable<string> permissions))
        {
            return permissions;
        }

        // Single database query to get all permissions
        permissions = await _context.Users
            .Where(u => u.Id == userId)
            .SelectMany(u => u.UserPermissions.Where(up => up.IsActive)
                .Select(up => up.Permission.Name)
                .Union(u.UserRoles.SelectMany(ur => ur.Role.RolePermissions
                    .Select(rp => rp.Permission.Name))))
            .Distinct()
            .ToListAsync();

        _cache.Set(cacheKey, permissions, TimeSpan.FromMinutes(30));
        
        return permissions;
    }

    public async Task PreloadUserPermissionsAsync(string userId)
    {
        await GetUserPermissionsAsync(userId);
    }
}

15. Testing Authorization

Unit Testing Authorization Handlers

public class DocumentAuthorizationHandlerTests
{
    private DocumentAuthorizationHandler _handler;
    private Mock<UserManager<ApplicationUser>> _userManagerMock;

    [SetUp]
    public void Setup()
    {
        _userManagerMock = new Mock<UserManager<ApplicationUser>>(
            Mock.Of<IUserStore<ApplicationUser>>(), null, null, null, null, null, null, null, null);
        
        _handler = new DocumentAuthorizationHandler(_userManagerMock.Object);
    }

    [Test]
    public async Task HandleRequirementAsync_AuthorCanEditOwnDocument_Succeeds()
    {
        // Arrange
        var document = new Document { AuthorId = "user123", Status = DocumentStatus.Draft };
        var requirement = new DocumentAuthorizationRequirement("Edit");
        var user = CreateUser("user123", "User");
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, document);

        // Act
        await _handler.HandleAsync(context);

        // Assert
        Assert.IsTrue(context.HasSucceeded);
    }

    [Test]
    public async Task HandleRequirementAsync_NonAuthorCannotEditDocument_Fails()
    {
        // Arrange
        var document = new Document { AuthorId = "user123", Status = DocumentStatus.Draft };
        var requirement = new DocumentAuthorizationRequirement("Edit");
        var user = CreateUser("user456", "User"); // Different user
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, document);

        // Act
        await _handler.HandleAsync(context);

        // Assert
        Assert.IsFalse(context.HasSucceeded);
    }

    [Test]
    public async Task HandleRequirementAsync_AdminCanEditAnyDocument_Succeeds()
    {
        // Arrange
        var document = new Document { AuthorId = "user123", Status = DocumentStatus.Draft };
        var requirement = new DocumentAuthorizationRequirement("Edit");
        var user = CreateUser("admin123", "Admin"); // Admin user
        var context = new AuthorizationHandlerContext(new[] { requirement }, user, document);

        // Act
        await _handler.HandleAsync(context);

        // Assert
        Assert.IsTrue(context.HasSucceeded);
    }

    private ClaimsPrincipal CreateUser(string userId, string role)
    {
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, userId),
            new Claim(ClaimTypes.Role, role)
        };
        
        return new ClaimsPrincipal(new ClaimsIdentity(claims, "Test"));
    }
}

Integration Testing Authorization

public class AuthorizationIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;

    public AuthorizationIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
    }

    [Test]
    public async Task AdminEndpoint_WithAdminUser_ReturnsSuccess()
    {
        // Arrange
        var client = _factory.CreateClient();
        
        // Create admin user and get token
        var token = await GetAdminTokenAsync();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        // Act
        var response = await client.GetAsync("/api/admin/users");

        // Assert
        response.EnsureSuccessStatusCode();
    }

    [Test]
    public async Task AdminEndpoint_WithRegularUser_ReturnsForbidden()
    {
        // Arrange
        var client = _factory.CreateClient();
        
        // Create regular user and get token
        var token = await GetRegularUserTokenAsync();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        // Act
        var response = await client.GetAsync("/api/admin/users");

        // Assert
        Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
    }

    private async Task<string> GetAdminTokenAsync()
    {
        // Implementation to get JWT token for admin user
        // This would typically call your authentication endpoint
        return "admin_jwt_token";
    }

    private async Task<string> GetRegularUserTokenAsync()
    {
        // Implementation to get JWT token for regular user
        return "user_jwt_token";
    }
}

16. Troubleshooting & Debugging

Authorization Diagnostics

public class AuthorizationDiagnosticsMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<AuthorizationDiagnosticsMiddleware> _logger;

    public AuthorizationDiagnosticsMiddleware(RequestDelegate next,
        ILogger<AuthorizationDiagnosticsMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        // Log authorization attempts
        var originalBodyStream = context.Response.Body;
        
        using var responseBody = new MemoryStream();
        context.Response.Body = responseBody;

        await _next(context);

        // Log authorization results
        if (context.Response.StatusCode == 403 || context.Response.StatusCode == 401)
        {
            _logger.LogWarning(
                "Authorization failed - Path: {Path}, User: {User}, Status: {Status}",
                context.Request.Path,
                context.User.Identity.Name,
                context.Response.StatusCode);
        }

        responseBody.Seek(0, SeekOrigin.Begin);
        await responseBody.CopyToAsync(originalBodyStream);
    }
}

// Custom authorization event source
public class AuthorizationEvents
{
    public static readonly EventHandler<AuthorizationFailedEventArgs> AuthorizationFailed = 
        (sender, e) =>
        {
            var logger = (ILogger<AuthorizationEvents>)sender;
            logger.LogWarning(
                "Authorization failed for user {User} on resource {Resource}. Requirement: {Requirement}",
                e.Context.User.Identity.Name,
                e.Context.Resource?.GetType().Name,
                e.Requirement.GetType().Name);
        };
}

Common Issues and Solutions

// Problem: Policies not being evaluated
// Solution: Ensure policies are registered and handlers are scoped
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy("CustomPolicy", policy =>
            policy.Requirements.Add(new CustomRequirement()));
    });

    // Register authorization handlers
    services.AddScoped<IAuthorizationHandler, CustomRequirementHandler>();
}

// Problem: Resource-based authorization not working
// Solution: Ensure resource is passed to AuthorizeAsync
public async Task<IActionResult> EditDocument(int id)
{
    var document = await _documentService.GetDocumentAsync(id);
    
    // Correct: Pass the document resource
    var result = await _authorizationService.AuthorizeAsync(User, document, "EditPolicy");
    
    // Incorrect: Not passing the resource
    // var result = await _authorizationService.AuthorizeAsync(User, "EditPolicy");
    
    if (!result.Succeeded)
        return Forbid();
    
    return View(document);
}

17. Real-World Implementation Example

Complete E-commerce Authorization System

// Product entity with authorization properties
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public decimal Price { get; set; }
    public string VendorId { get; set; } // User ID of the vendor
    public ProductStatus Status { get; set; }
    public DateTime CreatedDate { get; set; }
    public string Category { get; set; }
    public bool IsFeatured { get; set; }
}

public enum ProductStatus
{
    Draft,
    Published,
    Discontinued,
    Archived
}

// Comprehensive product authorization requirements
public class ProductAuthorizationRequirement : IAuthorizationRequirement
{
    public string Operation { get; }

    public ProductAuthorizationRequirement(string operation)
    {
        Operation = operation;
    }
}

public class ProductAuthorizationHandler 
    : AuthorizationHandler<ProductAuthorizationRequirement, Product>
{
    private readonly IPermissionService _permissionService;

    public ProductAuthorizationHandler(IPermissionService permissionService)
    {
        _permissionService = permissionService;
    }

    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        ProductAuthorizationRequirement requirement,
        Product product)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        switch (requirement.Operation)
        {
            case "View":
                await HandleViewRequirement(context, product, userId);
                break;
                
            case "Edit":
                await HandleEditRequirement(context, product, userId);
                break;
                
            case "Delete":
                await HandleDeleteRequirement(context, product, userId);
                break;
                
            case "Publish":
                await HandlePublishRequirement(context, product, userId);
                break;
                
            case "Feature":
                await HandleFeatureRequirement(context, product, userId);
                break;
        }
    }

    private async Task HandleViewRequirement(AuthorizationHandlerContext context, 
        Product product, string userId)
    {
        // Anyone can view published products
        if (product.Status == ProductStatus.Published)
        {
            context.Succeed(new ProductAuthorizationRequirement("View"));
            return;
        }

        // Vendor can view their own products
        if (product.VendorId == userId)
        {
            context.Succeed(new ProductAuthorizationRequirement("View"));
            return;
        }

        // Admins and moderators can view all products
        if (context.User.IsInRole("Admin") || context.User.IsInRole("Moderator"))
        {
            context.Succeed(new ProductAuthorizationRequirement("View"));
        }
    }

    private async Task HandleEditRequirement(AuthorizationHandlerContext context,
        Product product, string userId)
    {
        // Vendor can edit their own non-archived products
        if (product.VendorId == userId && product.Status != ProductStatus.Archived)
        {
            context.Succeed(new ProductAuthorizationRequirement("Edit"));
            return;
        }

        // Admins can edit any product
        if (context.User.IsInRole("Admin"))
        {
            context.Succeed(new ProductAuthorizationRequirement("Edit"));
            return;
        }

        // Moderators can edit products in their category
        if (context.User.IsInRole("Moderator") && 
            await HasCategoryPermissionAsync(userId, product.Category))
        {
            context.Succeed(new ProductAuthorizationRequirement("Edit"));
        }
    }

    private async Task HandleDeleteRequirement(AuthorizationHandlerContext context,
        Product product, string userId)
    {
        // Vendor can delete their own draft products
        if (product.VendorId == userId && product.Status == ProductStatus.Draft)
        {
            context.Succeed(new ProductAuthorizationRequirement("Delete"));
            return;
        }

        // Admins can delete any product
        if (context.User.IsInRole("Admin"))
        {
            context.Succeed(new ProductAuthorizationRequirement("Delete"));
        }
    }

    private async Task HandlePublishRequirement(AuthorizationHandlerContext context,
        Product product, string userId)
    {
        // Vendor can publish their own products if they have permission
        if (product.VendorId == userId && 
            await _permissionService.HasPermissionAsync(userId, "PublishProducts"))
        {
            context.Succeed(new ProductAuthorizationRequirement("Publish"));
            return;
        }

        // Admins and moderators can publish any product
        if (context.User.IsInRole("Admin") || context.User.IsInRole("Moderator"))
        {
            context.Succeed(new ProductAuthorizationRequirement("Publish"));
        }
    }

    private async Task HandleFeatureRequirement(AuthorizationHandlerContext context,
        Product product, string userId)
    {
        // Only admins and users with feature permission can feature products
        if (context.User.IsInRole("Admin") || 
            await _permissionService.HasPermissionAsync(userId, "FeatureProducts"))
        {
            context.Succeed(new ProductAuthorizationRequirement("Feature"));
        }
    }

    private async Task<bool> HasCategoryPermissionAsync(string userId, string category)
    {
        var permissionName = $"Moderate{category}Category";
        return await _permissionService.HasPermissionAsync(userId, permissionName);
    }
}

// Complete products controller with authorization
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    private readonly IAuthorizationService _authorizationService;

    public ProductsController(IProductService productService,
        IAuthorizationService authorizationService)
    {
        _productService = productService;
        _authorizationService = authorizationService;
    }

    [HttpGet]
    [AllowAnonymous]
    public async Task<ActionResult<IEnumerable<ProductDto>>> GetProducts(
        [FromQuery] ProductQueryParameters parameters)
    {
        // Apply filters based on user permissions
        if (!User.Identity.IsAuthenticated)
        {
            parameters.Status = ProductStatus.Published;
        }
        else
        {
            var canViewAll = await _authorizationService.AuthorizeAsync(
                User, new ProductAuthorizationRequirement("ViewAll"));
            
            if (!canViewAll.Succeeded)
            {
                parameters.VendorId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
            }
        }

        var products = await _productService.GetProductsAsync(parameters);
        return Ok(products);
    }

    [HttpPost]
    [Authorize(Policy = "CreateProduct")]
    public async Task<ActionResult<ProductDto>> CreateProduct(CreateProductDto createDto)
    {
        var product = await _productService.CreateProductAsync(createDto, User);
        return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product);
    }

    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(int id, UpdateProductDto updateDto)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, product, new ProductAuthorizationRequirement("Edit"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.UpdateProductAsync(id, updateDto);
        return NoContent();
    }

    [HttpPost("{id}/publish")]
    public async Task<IActionResult> PublishProduct(int id)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, product, new ProductAuthorizationRequirement("Publish"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.PublishProductAsync(id);
        return NoContent();
    }

    [HttpPost("{id}/feature")]
    public async Task<IActionResult> FeatureProduct(int id, bool isFeatured)
    {
        var product = await _productService.GetProductAsync(id);
        
        if (product == null)
            return NotFound();

        var authorizationResult = await _authorizationService.AuthorizeAsync(
            User, product, new ProductAuthorizationRequirement("Feature"));

        if (!authorizationResult.Succeeded)
            return Forbid();

        await _productService.SetFeaturedAsync(id, isFeatured);
        return NoContent();
    }
}

18. Conclusion

Advanced authorization in ASP.NET Core provides a robust, flexible framework for securing your applications. By mastering policy-based authorization, resource protection, and custom requirements, you can implement sophisticated security models that match your business needs.

Key Takeaways

  1. Policy-based authorization offers declarative security that's easy to maintain and test

  2. Resource-based authorization enables fine-grained control over specific data entities

  3. Custom requirements and handlers allow implementation of complex business rules

  4. Claims transformation provides dynamic permission assignment

  5. Performance optimization through caching and efficient querying is crucial for scalability

  6. Comprehensive testing ensures your authorization logic works as expected

Continuing Your Journey

In our next post, we'll dive into API Development in ASP.NET Core, covering RESTful principles, API versioning, documentation, and advanced API patterns.

Remember: Security is not a feature you add at the end—it's a fundamental aspect that should be designed into your application from the beginning. The authorization patterns we've covered provide the foundation for building secure, enterprise-ready applications.