Files
chesscubing/ChessCubing.App/Services/KeycloakAccountFactory.cs

54 lines
1.9 KiB
C#

using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication.Internal;
namespace ChessCubing.App.Services;
public sealed class KeycloakAccountFactory(IAccessTokenProviderAccessor accessor)
: AccountClaimsPrincipalFactory<RemoteUserAccount>(accessor)
{
public override async ValueTask<ClaimsPrincipal> CreateUserAsync(
RemoteUserAccount account,
RemoteAuthenticationUserOptions options)
{
var user = await base.CreateUserAsync(account, options);
if (user.Identity is not ClaimsIdentity identity || !identity.IsAuthenticated)
{
return user;
}
var roleClaimType = options.RoleClaim ?? "role";
var existingRoles = identity.FindAll(roleClaimType)
.Select(claim => claim.Value)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var role in ReadRealmRoles(account))
{
if (existingRoles.Add(role))
{
identity.AddClaim(new Claim(roleClaimType, role));
}
}
return user;
}
private static IEnumerable<string> ReadRealmRoles(RemoteUserAccount account)
{
if (!account.AdditionalProperties.TryGetValue("realm_access", out var realmAccessValue) ||
realmAccessValue is not JsonElement realmAccessElement ||
realmAccessElement.ValueKind != JsonValueKind.Object ||
!realmAccessElement.TryGetProperty("roles", out var rolesElement) ||
rolesElement.ValueKind != JsonValueKind.Array)
{
return [];
}
return rolesElement.EnumerateArray()
.Where(item => item.ValueKind == JsonValueKind.String)
.Select(item => item.GetString())
.OfType<string>();
}
}