Skip to content

how it works authorization

Shaylen Reddy edited this page Jan 20, 2024 · 2 revisions

Authorization

Now that authentication is out the way, it's time to explain the different types of authorization used in this solution

There's two:

  • Role-Based Access Control [RBAC]
    • The role comes from the user's claims in the id_token and then configured to include in the access_token in order to pass it downstream
  • Policy-Based Access Control [PBAC]
    • These are custom, and can include roles, assertions or your own custom requirements

How It's Invoked

Controllers and/or actions decorated with the [Authorize] attribute require authentication

Authorize attributes can also optionally specify roles [Authorize(Roles = "Administrator")] or policies [Authorize(Policy = "PolicyName")]

Examples in the Solution

The Admin Controller for the Admin Portal

This uses Role-Based Access Control

[Authorize(Roles = "Administrator")]
public class AdminController : Controller
{
    // ...
}

The Addresses Controller at the Address Service

This uses Policy-Based Access Control

[Route("api/customers/{customerId}/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "MustBeARegularCustomer")]
public class AddressesController : ControllerBase
{
    // ...
}

How Policies Are Built

Since the MustBeARegularCustomer policy is used as an example, we'll have a look at how it's built

Here's the code in Program.cs that adds authorization and adds the policy

builder.Services.AddTransient<IAuthorizationHandler, CustomerIdFromClaimsMustMatchCustomerIdFromRouteHandler>();

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("MustBeARegularCustomer", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.RequireAssertion(context => context.User.IsInRole("Administrator") is false);
        policy.AddRequirements(
            new CustomerIdFromClaimsMustMatchCustomerIdFromRouteRequirement());
    });

It's built with one custom requirement CustomerIdFromClaimsMustMatchCustomerIdFromRouteRequirement

This means that custom logic is needed in order to be authorized to access resources provided by the AddressesController

Before this was in place, that custom logic was within the methods after the user was authorized, and isn't the best behavior

Users should be evaluated before even reaching the code within a method

A requirement implements the IAuthorizationRequirement interface and may contain parameters for the requirement, however, the logic isn't implemented here

A dedicated handler is created - CustomerIdFromClaimsMustMatchCustomerIdFromRouteHandler - that implements the AuthorizationHandler<T>, T in this case is CustomerIdFromClaimsMustMatchCustomerIdFromRouteRequirement

This is where the custom evaluation occurs and determines if a user is authorized or not

View the full implementation here

Custom authorization handlers have to be added to the services collection using any service lifetime [according to the docs]

builder.Services.AddTransient<IAuthorizationHandler, CustomerIdFromClaimsMustMatchCustomerIdFromRouteHandler>();
Clone this wiki locally