Executando verificação de segurança...
2

Map Multiple Roles - Blazor Webassembly

Problema:

Estava implementando a Autorização em uma aplicação Blazor WebAssembly, com isso estava querendo realizar uma Policy de acordo com as roles que vier no meu Token JWT, quando receber um Role com um valor X ele terá os acessos, de acordo com a regra negocial.

Ok, o Token de exemplo:

{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022;,
  "role": [
     "Admin",
     "Basico"
   ]
}

Fazendo a autenticação e a autorização, eu consigo acessar o context.User, porém ao ver o valor da claim do type Role, ela tem um valor multiplo, ou seja um Array de String. O certo seria o Blazor (ASP NET) interpretar cada uma como uma Claim do tipo Role, porém não é o que acontece.

Ela chega uma claim apenas com o valor assim:

  • Claim[3]
    Type: "role”
    Value: "["Admin”,"Basico”]”

O ideal seria assim

  • Claim[3]
    Type: "role”
    Value: "Admin”

  • Claim[4]
    Type: "role”
    Value: "Basico”

Por isso segue uma forma de solucionar.


Solução:

O problema é que o Blazor não decompõe o array de funções, ele apenas pega o texto bruto e o interpreta como o nome da função, em vez de um objeto JSON que ele precisa manipular. Blazor espera várias declarações de função com o mesmo tipo, uma função por declaração.

Para decompor a declaração de função, é necessário criar uma fábrica personalizada.

CustomUserFactory.cs

using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
using System.Security.Claims;
using System.Text.Json;

namespace PCDF.Personas.Shared;

public class CustomUserFactory : AccountClaimsPrincipalFactory<RemoteUserAccount>
{
    public CustomUserFactory(IAccessTokenProviderAccessor accessor)
        : base(accessor)
    {
    }

    public async override ValueTask<ClaimsPrincipal> CreateUserAsync(
        RemoteUserAccount account,
        RemoteAuthenticationUserOptions options)
    {
        var user = await base.CreateUserAsync(account, options);
        var claimsIdentity = (ClaimsIdentity?)user.Identity;

        if (account != null && claimsIdentity != null)
        {
            MapArrayClaimsToMultipleSeparateClaims(account, claimsIdentity);
        }

        return user;
    }

    private void MapArrayClaimsToMultipleSeparateClaims(RemoteUserAccount account, ClaimsIdentity claimsIdentity)
    {
        foreach (var prop in account.AdditionalProperties)
        {
            var key = prop.Key;
            var value = prop.Value;
            if (value != null && (value is JsonElement element && element.ValueKind == JsonValueKind.Array))
            {
                // Remove the Roles claim with an array value and create a separate one for each role.
                claimsIdentity.RemoveClaim(claimsIdentity.FindFirst(prop.Key));
                var claims = element.EnumerateArray().Select(x => new Claim(prop.Key, x.ToString()));
                claimsIdentity.AddClaims(claims);
            }
        }
    }
}

Em seguida, você o adiciona aos serviços imediatamente após a chamada AddOidcAuthentication.

Program.cs

builder.Services.AddApiAuthorization().AddAccountClaimsPrincipalFactory<CustomUserFactory>();

Se não expliquei muito bem, pois quis ser mais direto para quando eu precisar novamente, ter isso mais rápido, vou deixar o link que achei para solucionar.

Caso queira ver o link que encontrei realmente a solução está aqui, Créditos ao: https://code-maze.com/using-roles-in-blazor-webassembly-hosted-applications/

Carregando publicação patrocinada...