Skip to content

Commit fb8af56

Browse files
authored
Add files via upload
1 parent c3f1cac commit fb8af56

1 file changed

Lines changed: 118 additions & 0 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
title: WebAssembly app error - These requirements were not met RolesAuthorizationRequirement in Microsoft Entra ID
3+
description: Provides guidance for troubleshooting role-based access control issues in WebAssembly authentication apps
4+
ms.date: 12/26/2024
5+
ms.author: willfid
6+
ms.service: entra-id
7+
ms.custom: sap:Microsoft Entra App Integration and Development
8+
---
9+
10+
# Add role claim support in WebAssembly authentication in Microsoft Entra ID
11+
12+
This article provides guidance to resolve role-based access control issues in developing WebAssembly authentication apps.
13+
14+
## Symptoms
15+
16+
When you build a WebAssembly authentication app and try to implement role-based access control in the app, you receive the following error messages:
17+
- You are not authorized to access this resource.
18+
- Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]Authorization failed. These requirements were not met: RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (ROLE_NAME)
19+
20+
## Cause
21+
22+
The WebAssembly authentication stack might cast role claims into a single string. This prevents proper role-based access control.
23+
24+
### Solution
25+
26+
You can implement a custom user factory to modify the behavior of role claims mapping. To do this, follow these steps.
27+
28+
#### Step 1: Create a custom user factory
29+
30+
Create a custom User Factory (CustomUserFactory.cs):
31+
32+
```csharp
33+
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
34+
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
35+
using System.Security.Claims;
36+
using System.Text.Json;
37+
38+
public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
39+
{
40+
public CustomUserFactory(IAccessTokenProviderAccessor accessor)
41+
: base(accessor)
42+
{
43+
}
44+
45+
public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
46+
RemoteUserAccount account,
47+
RemoteAuthenticationUserOptions options)
48+
{
49+
var user = await base.CreateUserAsync(account, options);
50+
var claimsIdentity = (ClaimsIdentity?)user.Identity;
51+
52+
if (account != null && claimsIdentity != null)
53+
{
54+
MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
55+
}
56+
57+
return user;
58+
}
59+
60+
private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
61+
{
62+
foreach (var prop in account.AdditionalProperties)
63+
{
64+
var key = prop.Key;
65+
var value = prop.Value;
66+
if (value != null && (value is JsonElement element && element.ValueKind == JsonValueKind.Array))
67+
{
68+
// Remove the Roles claim with an array value, and create new claims with the same key
69+
claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(prop.Key));
70+
var claims = element.EnumerateArray().Select(x => new Claim(prop.Key, x.ToString()));
71+
claimsIdentity.AddClaims(claims);
72+
}
73+
}
74+
}
75+
}
76+
```
77+
78+
#### Step 2: Add role mapping and custom user factory to your authentication middleware
79+
80+
If you're using `AddOidcAuthentication`:
81+
82+
```csharp
83+
84+
builder.Services.AddOidcAuthentication(options =>
85+
{
86+
builder.Configuration.Bind("AzureAd", options.ProviderOptions);
87+
options.ProviderOptions.AdditionalProviderParameters.Add("domain_hint", "contoso.com");
88+
options.ProviderOptions.DefaultScopes.Add("User.Read");
89+
options.UserOptions.RoleClaim = "roles";
90+
options.ProviderOptions.ResponseType = "code";
91+
}).AddAccountClaimsPrincipalFactory<CustomUserFactory>();
92+
```
93+
94+
If you're using `AddMsalAuthentication`:
95+
96+
```csharp
97+
builder.Services.AddMsalAuthentication(options =>
98+
{
99+
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
100+
options.ProviderOptions.AdditionalScopesToConsent.Add("user.read");
101+
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://{your-api-id}");
102+
options.UserOptions.RoleClaim = "roles";
103+
}).AddAccountClaimsPrincipalFactory<CustomUserFactory>();
104+
```
105+
106+
#### Step 3: Add the `Authorize` attribute to Blazor pages
107+
108+
```csharp
109+
@attribute [Authorize(Roles="access_as_user")]
110+
```
111+
112+
Next, add app roles to your app registration, assign a user to the app role, and then configure your app to use the assigned app role.
113+
114+
## References
115+
116+
- [Secure an ASP.NET Core Blazor WebAssembly standalone app with the Authentication library](/aspnet/core/blazor/security/webassembly/standalone-with-authentication-library)
117+
118+
- [GitHub project: Sample that has this solution implemented](https://github.com/willfiddes/aad-webassembly-auth)

0 commit comments

Comments
 (0)