Using Microsoft Entra groups to authorize resources access in Blazor web assembly


Recently, as a part of one of my pet projects, I was trying to implement authentication and authorization with Microsoft Entra identities and groups in a Blazor WebAssembly application. The part related to authentication was quite a simple one. The tricky part started when I tried to provide access to the specific page only for members of a specific Microsoft Entra group.
The first thing I did was configuring Token for my App Registration in Microsoft Entra to contain information about groups membership. I won’t be describing this process here, you are able to find this information inside many internet articles (for example: here). After that, I’ve added the following line of code inside Program.cs file in my WebAssembly project:
{
config.AddPolicy("GroupPolicy", policy =>
policy.RequireClaim("groups", "e39713a4-3701-5fa2-b407-17221baf91bc"));
});
Don’t worry, the all the GUID identifiers you can see in this article are the fake ones, used only for the purpose of this text ;).
I’ve also added the following attribute into the page I’ve tried to restrict access to:
@attribute [Authorize(Policy = "GroupPolicy")]
Unfortunately, when I tried to access the page, after successful login with the group member account, I was still getting the following information: “You are not authorized to access this resource.”. I the browser console, the following information was available:
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. These requirements were not met: ClaimsAuthorizationRequirement:Claim.Type=groups and Claim.Value is one of the following values: (e39713a4-3701-5fa2-b407-17221baf91bc)
That was really strange. I’ve quickly decoded my token retrieved with browser tools using https://jwt.ms site. This is what I’ve found inside:
"groups": [ "a9e91909-1ed9-467d-9d08-37379a10c8f4", "5e558337-34f1-4efc-9433-bbb7b74b67d9", "cced8cf7-26c6-4abc-bc32-7824848f521c", "e39713a4-3701-5fa2-b407-17221baf91bc" ]
That makes sense cause my account is a member of the multiple Entra groups. However due to some reasons Microsoft Identity frameworks treat this value as a single string and try to compare its content with my claim policy value.
What was needed was implementing custom Authorization Requirement and its handler. I’ve achieved this goal with the 2 following classes:
public class GroupRequirement : IAuthorizationRequirement
{
public string RequiredGroupId { get; }
public GroupRequirement(string groupId)
{
RequiredGroupId = groupId;
}
}
public class GroupRequirementHandler : AuthorizationHandler<GroupRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, GroupRequirement requirement)
{
var groupClaims = context.User.Claims.Where(c => c.Type == "groups").Select(c => c.Value).FirstOrDefault();
if(groupClaims != null)
{
var deserializedClaims = JsonSerializer.Deserialize<List<string>>(groupClaims);
if(deserializedClaims != null)
{
if (deserializedClaims.Contains(requirement.RequiredGroupId))
{
context.Succeed(requirement);
}
}
}
return Task.CompletedTask;
}
}
Finally I’ve modified the Program.cs file in my project to use my custom Group Requirement class.
builder.Services.AddAuthorizationCore(config =>
{
config.AddPolicy("GroupPolicy", policy =>
policy.Requirements.Add(new GroupRequirement("e39713a4-3701-5fa2-b407-17221baf91bc ")));
});
After all the above described operations I was finally able to access the secured page.
Subscribe to my newsletter
Read articles from Piotr Gaszewski directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
