5
votes

I am trying to set up AAD Authorization based on user defined roles following this doc https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/azure-active-directory-groups-and-roles?view=aspnetcore-3.1#user-defined-roles I am able to set it up in the app manifest and get the API authorization working. However when I try to do it on the UI side I cannot get the claim to appear. I did the json interpreting classes (DirectoryObjects, CustomUserAccount and Value(used by directory object)). I also added the CustomUserFactory removing the group stuff since I only care about roles:

        private readonly ILogger<CustomUserFactory> _logger;
        private readonly IHttpClientFactory _clientFactory;

        public CustomUserFactory(IAccessTokenProviderAccessor accessor,
            IHttpClientFactory clientFactory,
            ILogger<CustomUserFactory> logger)
            : base(accessor)
        {
            _clientFactory = clientFactory;
            _logger = logger;
        }

        public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
            CustomUserAccount account,
            RemoteAuthenticationUserOptions options)
        {
            var initialUser = await base.CreateUserAsync(account, options);

            if (initialUser.Identity.IsAuthenticated)
            {
                var userIdentity = (ClaimsIdentity)initialUser.Identity;

                foreach (var role in account.Roles)
                {
                    userIdentity.AddClaim(new Claim("role", role));
                }

                
            }

            return initialUser;
        }

and then I modified the program.cs as the doc mentioned:

    builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
    CustomUserAccount>(options =>
    {
        builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
        options.ProviderOptions.DefaultAccessTokenScopes.Add("apiaccessguid");
        options.UserOptions.RoleClaim = "role";
    }).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount,
    CustomUserFactory>();

when that didn't work I tried adding it as a policy with no luck as well:

 builder.Services.AddAuthorizationCore(options =>
    {
        options.AddPolicy("Admin", policy =>
            policy.RequireClaim("role", "admin"));
    });

for restricting the view I tried in code with the user.IsInRole("admin") and in the UI with

<AuthorizeView Roles="admin">
    <li class="nav-item px-3">
        <NavLink class="nav-link" href="Admin">
            Admin
        </NavLink>
    </li>
</AuthorizeView>

and with policy:

<AuthorizeView Policy="Admin">
    <Authorized>
        <p>
            The user is in the 'Administrator' AAD Administrative Role
            and can see this content.
        </p>
    </Authorized>
    <NotAuthorized>
        <p>
            The user is NOT in the 'Administrator' role and sees this
            content.
        </p>
    </NotAuthorized>
</AuthorizeView>

and none of them worked. Is there something I am missing? I also verified that the token has the admin role.

2
Aren't you missing a "s" to "role" ? The documentation states that the authenticated users will receive their assigned roles in the roles claim. - Loul G.
I tried that with no success, I think role is the right one since in the end of the CustomUserFactory i do a forloop of the roles and I add it with the claim of "role" which is what it said to do in this docs docs.microsoft.com/en-us/aspnet/core/blazor/security/… - Tacot

2 Answers

1
votes

I got it working by using RequireRole in the policy options.

For example, I added the app role to the manifest:

"appRoles": [
    {
        "allowedMemberTypes": [
            "User"
        ],
        "description": "Reader role.",
        "displayName": "Reader",
        "id": "41d9ba42-456e-4471-8946-24216e5f6c64",
        "isEnabled": true,
        "lang": null,
        "origin": "Application",
        "value": "Reader"
    }
]

Configure the policy via RequireRole:

builder.Services.AddAuthorizationCore(options =>
{
    options.AddPolicy("app-reader", policy => policy.RequireRole("Reader"));
});

And then use as per:

<AuthorizeView Policy="app-reader">
    <Authorized>
        <p>
            The user is in the 'Reader' Role
            and can see this content.
        </p>
    </Authorized>
    <NotAuthorized>
        <p>
            The user is NOT in the 'Reader' role 
            and sees this content.
        </p>
    </NotAuthorized>
</AuthorizeView>

Or, as an attribute on a razor page:

@attribute [Authorize(Policy = "app-reader")]
1
votes

Found my problem, the code was fine, the problem was on the Azure registration side, The client uses the roles registered in the client app in Azure while the server uses the ones in the server app. so make sure you register the user in both with the same role.