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

Piotr GaszewskiPiotr Gaszewski
2 min read

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.

0
Subscribe to my newsletter

Read articles from Piotr Gaszewski directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Piotr Gaszewski
Piotr Gaszewski