blazor-webassemblyasp.net-authorization

AuthorizationPolicy `.RequireRole()` does not work


my authorization policy does not work the way I expect it to. It's always evaluating false although all the conditions are met, as can be seen below. I have experimented in defining the role claim type name a few different ways because this is what I have seen most people do, but nothing seems to work. Does someone have an idea on how to proceed further with the debugging?

Program.cs

builder.Services.AddMsalAuthentication(options =>
{
    builder.Configuration.Bind("EntraExternalIdentities", options.ProviderOptions);
    // options.UserOptions.RoleClaim = "role";
    // options.UserOptions.RoleClaim = "roles";
    // options.UserOptions.RoleClaim = ClaimTypes.Role;
});

builder.Services.AddCascadingAuthenticationState();

builder.Services.AddAuthorizationCore(opt =>
{
    opt.AddPolicy("meh", policyBuilder => policyBuilder
            .RequireAuthenticatedUser()
            .RequireRole("admin")
            .Build());
});

View.razor

<AuthorizeView Policy="meh">
    <Authorized>
        <p>You are authenticated= @context.User.Identity.IsAuthenticated</p>
        <p>You ARE authorized.</p>
        <p>Hello, @context.User.Identity?.Name!</p>
        <pre>roles= @context.User.Claims.Single(s => s.Type == "roles").Value</pre>
    </Authorized>
    <NotAuthorized>
        <p>You are authenticated= @context.User.Identity.IsAuthenticated</p>
        <p>You're not authorized.</p>
        <p>Hello, @context.User.Identity?.Name!</p>
        <pre>roles= @context.User.Claims.Single(s => s.Type == "roles").Value</pre>
    </NotAuthorized>
</AuthorizeView>

enter image description here

The output in the DevTools console in Edge is Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. These requirements were not met:RolesAuthorizationRequirement:User.IsInRole must be true for one of the following roles: (admin)


Solution

  • If you take a look at what the RequireRole() does here, you'll see that it adds a RolesAuthorizationRequirement for given roles(s). If you look at it's HandleRequirementAsync, you can see it calls context.User.IsInRole(role) to check whether the user is in a given role.

    Further, if you look at the ClaimsPrincipal's IsInRole method here, you can see that it checks whether any identity contains a claim with key defined on that identity as a role claim with given value.

    So check your Identity, see what kind of RoleClaimType is defined on it and verify that given claim with given value is present on that identity.

    An easy way to debug this and see how your identity looks like when it's passed into the AuthorizationHandler is for example using RequireAssertion and placing a breakpoint there, e.g.

    builder.Services.AddAuthorizationCore(opt =>
    {
        opt.AddPolicy("meh", policyBuilder => policyBuilder
                //.RequireAuthenticatedUser()
                //.RequireRole("admin")
                .RequireAssertion(ctx =>
                {
                   // Some custom assertion for your "meh" policy
                   // Put a breakpoint here to check what is passed as context.
                   return ctx.User.Claims.Single(s => s.Type == "roles")?.Value == "admin";
                })
                .Build());
    });