Section 5 of 6

Resource-Based Authorization

🎯 What You'll Learn

  • What is resource-based authorization
  • IAuthorizationService
  • AuthorizeAsync()
  • Creating resource handlers
  • InvenTrack example

What is Resource-Based Authorization?

Resource-based authorization makes decisions based on the specific resource being accessed. For example, users can only edit their own products.

💡 Use Case

Use resource-based authorization when the authorization decision depends on the resource data itself, not just the user's role or claims.

IAuthorizationService

Inject IAuthorizationService to perform authorization checks in code.

Basic Usage C#
public class ProductsController : Controller
{
    private readonly IAuthorizationService _authorizationService;

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

    public async Task<IActionResult> Edit(int id)
    {
        var product = await _context.Products.FindAsync(id);

        var authResult = await _authorizationService.AuthorizeAsync(
            User, product, "CanEditProduct");

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

        return View(product);
    }
}

Creating Resource Handler

1. Define Requirement

ProductOwnerRequirement.cs C#
public class ProductOwnerRequirement : IAuthorizationRequirement
{
}

2. Create Handler

ProductOwnerHandler.cs C#
public class ProductOwnerHandler 
    : AuthorizationHandler<ProductOwnerRequirement, Product>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        ProductOwnerRequirement requirement,
        Product resource)
    {
        var userId = context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        if (resource.CreatedBy == userId)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

3. Register Handler and Policy

Program.cs C#
builder.Services.AddSingleton<IAuthorizationHandler, ProductOwnerHandler>();

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

Complete InvenTrack Example

Product Model C#
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; } = string.Empty;
    public string CreatedBy { get; set; } = string.Empty;
}
ProductsController.cs C#
[Authorize]
public class ProductsController : Controller
{
    private readonly IAuthorizationService _authorizationService;

    [HttpGet]
    public async Task<IActionResult> Edit(int id)
    {
        var product = await _context.Products.FindAsync(id);
        if (product == null)
            return NotFound();

        // Check if user can edit this specific product
        var authResult = await _authorizationService
            .AuthorizeAsync(User, product, "CanEditProduct");

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

        return View(product);
    }
}

Key Takeaways

  • Resource-based: Authorization depends on resource data
  • IAuthorizationService: Perform checks in code
  • AuthorizeAsync(): Check authorization for resource
  • IAuthorizationRequirement: Define requirement
  • AuthorizationHandler: Implement authorization logic
  • Use case: Users can only edit their own data