Skip to content

Commit 7f1168e

Browse files
committed
Create 2025-02-25-aspnetcore-authorizationservice-to-validate-policies.md
1 parent ec9cbd7 commit 7f1168e

File tree

1 file changed

+101
-0
lines changed

1 file changed

+101
-0
lines changed
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
layout: post
3+
title: "Handling Multiple ASP.NET Core Policies with Manual Authorization Checks"
4+
description: "When implementing authorization in ASP.NET Core, Microsoft provides several ways to check multiple policies. While using **multiple `[Authorize(Policy = "...")]` attributes** works in some or most cases, there are scenarios where a more flexible approach is needed."
5+
date: 2025-02-25 00:30
6+
author: Robert Muehsig
7+
tags: [ASP.NET Core]
8+
language: en
9+
---
10+
11+
{% include JB/setup %}
12+
13+
# ASP.NET Core Policies
14+
15+
[ASP.NET Core policies](https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies) provide a structured and reusable way to enforce authorization rules across your application.
16+
The built-in features are very flexible, but we had trouble with one scenario - but depending how you write your "Requirements" this might even be possible with the built-in features.
17+
Our approach was to use the authorization service to check certain policies manually - which works quite good!
18+
19+
# The Challenge: Combining Policies with OR Logic
20+
21+
In one of our API use cases, we needed to allow access **either** for certain clients (e.g., specific admininstrative applications) **or** for certain users in the database. The two approaches differ:
22+
23+
- **Client Authorization**: This is relatively straightforward and can be handled using the built-in `RequireClaim` approach.
24+
- **User Authorization**: This required checking database permissions, meaning a **custom authorization requirement** was necessary.
25+
26+
Since both authorization paths are valid, they need to be evaluated using **OR logic**: if **either** condition is met, access should be granted.
27+
28+
# Solution: Using the Authorization Service for Manual Policy Checks
29+
Instead of relying solely on `[Authorize]` attributes, we can leverage the **`IAuthorizationService`** to manually check policies in our code.
30+
31+
## Step 1: Define the Authorization Policies
32+
In `Program.cs`, we define multiple policies:
33+
34+
```csharp
35+
builder.Services.AddAuthorization(options =>
36+
{
37+
options.AddPolicy(KnownApiPolicies.AdminApiPolicyForServiceAccount, policy =>
38+
{
39+
policy.RequireClaim("scope", "admin-client");
40+
});
41+
options.AddPolicy(KnownApiPolicies.AdminApiPolicyForLoggedInUserAdministrator, policy =>
42+
{
43+
policy.Requirements.Add(new DbRoleRequirement(Custom.UserAdminInDatabase));
44+
});
45+
});
46+
```
47+
48+
## Step 2: Manually Validate User Authorization
49+
Using `IAuthorizationService`, we can manually check if the user meets **either** of the defined policies.
50+
51+
```csharp
52+
private async Task<AuthorizationResultType> ValidateUserAuthorization()
53+
{
54+
var user = User;
55+
56+
var serviceAccountAuth = await _authorizationService.AuthorizeAsync(user, KnownApiPolicies.AdminApiPolicyForServiceAccount);
57+
if (serviceAccountAuth.Succeeded)
58+
{
59+
return AuthorizationResultType.ServiceAccount;
60+
}
61+
62+
var userAuth = await _authorizationService.AuthorizeAsync(user, KnownApiPolicies.AdminApiPolicyForLoggedInUserAdministrator);
63+
if (userAuth.Succeeded)
64+
{
65+
return AuthorizationResultType.LoggedInUser;
66+
}
67+
68+
return AuthorizationResultType.None;
69+
}
70+
```
71+
72+
## Step 3: Apply the Authorization Logic in the Controller
73+
74+
```csharp
75+
[HttpGet]
76+
public async Task<ActionResult<...>> GetAsync()
77+
{
78+
var authResult = await ValidateUserAuthorization();
79+
80+
if (authResult == AuthorizationResultType.None)
81+
{
82+
return Forbid(); // Return 403 if unauthorized
83+
}
84+
85+
using var contextScope = authResult switch
86+
{
87+
AuthorizationResultType.ServiceAccount => // ... do something with the result,
88+
AuthorizationResultType.LoggedInUser => // ...,
89+
_ => throw new UnauthorizedAccessException()
90+
};
91+
92+
return Ok(_userService.GetUsers(...));
93+
}
94+
```
95+
96+
# Recap
97+
98+
We use the `IAuthorizationService.AuthorizeAsync` method to check multiple policies and depending on the outcome, we can handle it depending on our needs.
99+
This approach retains the same overall structure as the "default" policy-based authorization in ASP.NET Core but provides more flexibility by allowing policies to be evaluated dynamically via the service.
100+
101+
Keep in mind (as mentioned at the beginning): This is just one way of handling authorization. As far as we know, it works well without drawbacks while offering the flexibility we need.

0 commit comments

Comments
 (0)