Search code examples
azure-active-directoryblazoradditionblazor-webassemblyclaims

Blazor WASM Azure AD - Adding Custom Claims


I have a Blazer WASM app authenticating via Azure AD, and I need to fetch additional claims from a separate API.

I want to bring in these claims after Azure-AD authentication into the principle object so that I can perform simplified page [Authorization(role=.., policy=...)] checks.

In ASP.NET, one way of modifying claims is to register a custom IClaimsTransformation class which can be used to insert additional claims into the claims principle. Unfortunately, Blazor WASM with Azure AD authentication does not appear to support the use of IClaimsTransformation.

Do you know of a way to add additional claims into the existing claims principle?

NOTE: Another option I ruled out is using the new Azure Custom Claims provider, but it's in a preview state and I strongly wish to avoid going that route.


Solution

  • You can use the AddAccountClaimsPrincipalFactory method in order to add claims, in the Azure AD authenth process.

    public class CustomUserFactory : AccountClaimsPrincipalFactory<CustomUserAccount>
    {
        public CustomUserFactory(IAccessTokenProviderAccessor accessor)
            : base(accessor)
        {
    
        }
    
        public async override ValueTask<ClaimsPrincipal> CreateUserAsync(CustomUserAccount account, RemoteAuthenticationUserOptions options)
        {
            ClaimsPrincipal initialUser = await base.CreateUserAsync(account, options);
    
            if (initialUser?.Identity?.IsAuthenticated ?? false)
            {
                var userIdentity = (ClaimsIdentity)initialUser.Identity;
    
                userIdentity.AddClaim(new Claim("claim_type", "claim_value"));
            }
    
            return initialUser ?? new ClaimsPrincipal();
        }
    }
    
    public class CustomUserAccount : RemoteUserAccount
    {
        [JsonPropertyName("groups")]
        public string[] Groups { get; set; } = default!;
    
        [JsonPropertyName("roles")]
        public string[] Roles { get; set; } = default!;
    }
    

    And you will need to call AddAccountClaimsPrincipalFactory:

    public static IServiceCollection AddAuthentication(this IServiceCollection services,IConfiguration config)
    {
    
        services.AddMsalAuthentication<RemoteAuthenticationState, CustomUserAccount>(options =>
        {
            config.Bind("AzureAd", options.ProviderOptions.Authentication);
            options.ProviderOptions.Cache.CacheLocation = "localStorage";
            options.ProviderOptions.LoginMode = "redirect";
            options.ProviderOptions.DefaultAccessTokenScopes.Add(config["AzureAd:Scope"] ?? throw new ArgumentNullException("AzureAd:Scope"));
            options.UserOptions.RoleClaim = "role";
        }).AddAccountClaimsPrincipalFactory<RemoteAuthenticationState, CustomUserAccount, CustomUserFactory>();
    
    
        return services;
    }