Multiple authorization handlers for the same requirement in ASP.NET Core
Tags: aspnetcore, authorization, policybasedauthorization
Published: 2023-04-05 00:59:32

---
title: Multiple authorization handlers for the same requirement in ASP.NET Core
published: true
date: 2023-03-26 18:29:00 UTC
tags: aspnetcore,authorization,policybasedauthorization
canonical_url: https://coderethinked.com/multiple-authorization-handlers-for-the-same-requirement-in-asp-net-core/
---

We saw how we could set up policy-based authorization in our [previous](https://coderethinked.com/policy-based-authorization-in-asp-net-core/) article. In this article, we’ll focus on

- Need for multiple handlers for the same requirement
- How to setup multiple handlers for the same requirement (with demo)
- What would happen if we call `context.Fail` in the authorization handler if the user does not have access
- The execution order of multiple authorizations

We’ll use the same scenario as we used for our [policy-based authorization](https://coderethinked.com/policy-based-authorization-in-asp-net-core/): two lounges, one for premium users and one for standard users.

Let’s say we want to allow the trial/limited user to experience the standard and premium lounges (I know they are just images on our razor pages :P)

In this case, creating a new policy with both standard and premium access won’t help because we have to decorate the existing page model to have both the old policy and the new one. This adds an AND logic to the authorizations.

Let’s say we have added this to our code

```csharp
[Authorize(Policy = "LimitedUser")]
[Authorize(Policy = "StandardOnly")]
public class StandardLoungeModel : PageModel
{
    public void OnGet()
    {
    }
}
```

The authorization handlers now will only allow access to the standard page model only if the user has both the `LimitedUser` and `StandardOnly` access.

Technically, here we need an OR logic to allow either limited users or standard users. This is where the multiple handlers for the same requirement come in.

## Multiple handlers for the same requirements

To perform an OR-based evaluation, we will create multiple handlers for the same requirement so that we can bypass the user if the user has limited access. No need to create a new policy for the trial users.

Here is our original product access handler from our previous policy-based authorization post.

```csharp
public class ProductAccessHandler : AuthorizationHandler<ProductAccessRequirement>
{
    private readonly IUserAccessRepository _userAccessRepository;
    public ProductAccessHandler(IUserAccessRepository userAccessRepository)
    {
        _userAccessRepository = userAccessRepository;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ProductAccessRequirement requirement)
    {
        var user = ClaimExtensions.GetClaim(context.User, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "nameidentifier");
        if (user == null) {
          context.Fail();
          return Task.CompletedTask;
        }

        var hasAccess = _userAccessRepository.HasAccess(user.Value, requirement.ProductIds, CancellationToken.None);
        if (hasAccess) context.Succeed(requirement);

        return Task.CompletedTask;
    }
}
```

We will pass the user GUID and product Ids to the user access repository to see if the user has access to the resource. For more on how we set up the [policy-based authorization please check this blog post](https://dev.to/karthikchintala/policy-based-authorization-in-aspnet-core-2coe-temp-slug-8789261).

Now, Let’s create a new `TemporaryProductAccessHandler`.

```csharp
public class TemporaryProductAccessHandler : AuthorizationHandler<ProductAccessRequirement>
{
    private readonly IUserAccessRepository _userAccessRepository;

    public TemporaryProductAccessHandler(IUserAccessRepository userAccessRepository)
    {
        _userAccessRepository = userAccessRepository;
    }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ProductAccessRequirement requirement)
    {
        var user = ClaimExtensions.GetClaim(context.User, "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "nameidentifier");
        if (user == null) {
          context.Fail();
          return Task.CompletedTask;
        }

        var hasAccess = _userAccessRepository.HasAccess(user.Value, new string[]{ Constants.ProductCodes.LimitedUser }, CancellationToken.None);

        if (hasAccess)
            context.Succeed(requirement);
        
        return Task.CompletedTask;
    }
}
```

There’s a small difference between the product access handler and the temporary product access handler.

In our `TemporaryProductAccessHandler`, we will always pass in the `LimitedUser` as the product id to the `HasAccess` method instead of passing the product ids we get from the requirement so that the current user is evaluated against the limited user product access.

If the user has limited user as the product access, then we will invoke `context.Succeed(requirement)` to pass through the product access handler and the user should have access to the resource requested.

If an action method or a razor page model has decorated with `Authorize(Policy = "PremiumOnly")]` and the user just has limited access as product ID, the product access handler does not produce any success result but the `TemporaryProductAccessHandler` will check for limited user access and passes the authorization test.

This is how we can bypass the user having limited user access and provide access to all of the resources.

## Registering our handler for the same requirement

Registering is the same as how we register any service with its appropriate interface.

```csharp
builder.Services.AddSingleton<IAuthorizationHandler, ProductAccessHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, TemporaryProductAccessHandler>();
```

Once we registered our new `TemporaryProductAccessHandler`, we can simply run the app to see if the limited/temporary user is able to access the other pages. We don’t need to create new policies or add more Authorize attributes to the existing controllers or actions or razor page models.

Here is the demo of Limited user accessing both lounges

<video controls src="https://coderethinked.com/wp-content/uploads/2023/03/limited_user_gains_access_to_resources.mp4"></video>
_Limited users can access resources after multiple authorizations_

## If we invoked context.Fail() when a user does not have a product ID, what would happen?

Notice in our authorization handlers, we never called context.Fail() method though the user does not have access. We could do something like this in our `ProductAccessHandler`

```csharp
var hasAccess = _userAccessRepository.HasAccess(user.Value, requirement.ProductIds, CancellationToken.None);

if (hasAccess)
    context.Succeed(requirement);
else 
    context.Fail();
```

f we implement an `ProductAccessHandler` like this, we can’t bypass the temporary user access restriction because we’re configuring the authorization to deny access if the user fails the product access test.

**When any of the authorization handlers fail, access will be denied**. But **for authorization to succeed, at least one authorization handler must evaluate success**.

We had the following code in our authorization handlers.

```csharp
if (user == null) {
    context.Fail();
    return Task.CompletedTask;
}
```

If we encountered a null value for the user object we know the user is not logged in yet so we fail the authorization test.

But, in multiple authorizations, it’s better to ignore invoking `context.Fail()` when the user did not pass the authorization test because there could be another authorization handler that may check for attributes that could pass the authorization test.

## Execution order of multiple authorization handlers

Although, Microsoft documentation claims that the [handlers can execute in any order](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-7.0#what-should-a-handler-return).

![Execution order of authorization handlers](https://coderethinked.com/wp-content/uploads/2023/03/multiple-authorizations-execution-order-from-microsoft.png)

After debugging, I see the execution order of these handlers follows their order of registration in the `Program.cs` file.

So, if we registered `TemporaryProductAccessHandler` first and followed by `ProductAccessHandler`, the temporary handler gets fired first and product access gets the next hit.

```csharp
builder.Services.AddSingleton<IAuthorizationHandler, ProductAccessHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, TemporaryProductAccessHandler>();
```

Here is the debugging demo of the execution order.

<video controls src="https://coderethinked.com/wp-content/uploads/2023/03/multiple-auth-handlers-execution-order.mp4"></video>

Anyway, we are not worried about the order of execution in our article.

## References

- [Policy-based authorization on Coderethinked.com](https://coderethinked.com/policy-based-authorization-in-asp-net-core/)
- [Link to Github Repository](https://github.com/karthikchintala1/Authorizations/tree/multiple-handlers-for-same-requirement)

The post [Multiple authorization handlers for the same requirement in ASP.NET Core](https://coderethinked.com/multiple-authorization-handlers-for-the-same-requirement-in-asp-net-core/) appeared first on [Code Rethinked](https://coderethinked.com).