0

I have a client app developed in webassembly blazor and protected by Azure AD where its registered and 3 roles are defined and assigned to users.

Once a user sign-in the user is being redirect to profile page where set of claims are displayed

Claim Type            Value
oid                   091fadf9-b0bd-4583-b55d-XXXX
preferred_username    XX
roles                 ["Administrator"]

Based on user role different UI will be shown, however this didnt work

<AuthorizeView Roles="Administrator">
   ADMIN UI
</AuthorizeView>

my program class looks like this:

 public static async Task Main(string[] args)
        {
            var builder = WebAssemblyHostBuilder.CreateDefault(args);
            builder.RootComponents.Add<App>("app");
            //builder.Logging.SetMinimumLevel(LogLevel.Debug);


            ////builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
            builder.Services.AddScoped<CustomAuthorizationMessageHandler>();

            builder.Services.AddHttpClient("myAPI",
               client => client.BaseAddress = new Uri("private API URL"))
              .AddHttpMessageHandler<CustomAuthorizationMessageHandler>();

            builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
                .CreateClient("myAPI"));

           builder.Services.AddMsalAuthentication(options =>
            {
               builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
                options.ProviderOptions.DefaultAccessTokenScopes.Add(myAPI);
                options.UserOptions.RoleClaim = "roles";
            });

            await builder.Build().RunAsync();
        }

I have a supported class for user:

 public class UserClaimsBase: ComponentBase
    {
        // AuthenticationStateProvider service provides the current user's ClaimsPrincipal data.
        [Inject]
        private AuthenticationStateProvider AuthenticationStateProvider { get; set;
        }
        protected string _authMessage;
        protected IEnumerable<Claim> _claims = Enumerable.Empty<Claim>();

        // Defines list of claim types that will be displayed after successfull sign-in.
        private string[] returnClaims = { "name", "preferred_username", "tid", "oid", "roles" };

        protected override async Task OnInitializedAsync()
        {
            await GetClaimsPrincipalData();
        }

        /// <summary>
        /// Retrieves user claims for the signed-in user.
        /// </summary>
        /// <returns></returns>
        private async Task GetClaimsPrincipalData()
        {
            // Gets an AuthenticationState that describes the current user.
            var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();

            var user = authState.User;

            // Checks if the user has been authenticated.
            if (user.Identity.IsAuthenticated)
            {
                _authMessage = $"{user.Identity.Name} is authenticated.";

                // Sets the claims value in _claims variable.
                // The claims mentioned in returnClaims variable are selected only.
                _claims = user.Claims.Where(x => returnClaims.Contains(x.Type));
            }
            else
            {
                _authMessage = "The user is NOT authenticated.";
            }
        }
    }

What could be wrong here ? Im not sure what im missing ? Any help is appreciated.

1 Answer 1

1

Regarding the issue, please refer to the following steps

  1. Define appRole in your Azure AD application

add the following content in App manifest

"appRoles": [
        
        {
            "allowedMemberTypes": [
                "User",
                "Application"
            ],
            "description": "admin",
            "displayName": "Admin",
            "id": "d1c2ade8-98f8-45fd-aa4a-6d06b957c66f",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "value": "admin"
        },
        {
            "allowedMemberTypes": [
                "User",
                "Application"
            ],
            "description": "access database test",
            "displayName": "Test",
            "id": "d1c2ade8-98f8-45fd-aa4a-6d06b947c66f",
            "isEnabled": true,
            "lang": null,
            "origin": "Application",
            "value": "test"
        }
    ],
  1. Assign appRole to user

  2. Configure application

a. Package

 <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="3.2.1" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Build" Version="3.2.1" PrivateAssets="all" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="3.2.1" PrivateAssets="all" />
    <PackageReference Include="Microsoft.Authentication.WebAssembly.Msal" Version="3.2.1" />
    <PackageReference Include="System.Net.Http.Json" Version="3.2.0" />

b. Extend RemoteUserAccount to include properties for roles

using System;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;

public class CustomUserAccount : RemoteUserAccount
{
    [JsonPropertyName("roles")]
    public string[] Roles { get; set; } = Array.Empty<string>();

}

b. Define custom user factory to convert appRole claims

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Graph;

public class CustomAccountFactory
    : AccountClaimsPrincipalFactory<CustomUserAccount>
{
    private readonly ILogger<CustomAccountFactory> logger;
    private readonly IServiceProvider serviceProvider;

    public CustomAccountFactory(IAccessTokenProviderAccessor accessor,
        IServiceProvider serviceProvider,
        ILogger<CustomAccountFactory> logger)
        : base(accessor)
    {
        this.serviceProvider = serviceProvider;
        this.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("appRole", role));
            }
        }

        return initialUser;
    }
}

c. configure Azure AD auth in Program.cs

 builder.Services.AddMsalAuthentication<RemoteAuthenticationState,
                 CustomUserAccount>(options =>
                 {
                     builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
                    
                     options.UserOptions.RoleClaim = "appRole";
                 }).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount,
             CustomAccountFactory>();
  1. Auth
# add the following code in the page according to your need
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
  1. Test

My page

@page "/counter"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "admin")]
<h1>Counter</h1>

<p>Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount = 0;

    private async Task IncrementCount()
    {

        currentCount++;
    }
}

My user enter image description here

Result enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

This is the most straightforward answer. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.