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(accessor) { public override async ValueTask 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 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(); } }