using System.Net.Http.Headers; using System.Security.Claims; using System.Text.Json; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Authorization; namespace BlazorWebAssembly.Providers { public class JwtAuthenticationStateProvider : AuthenticationStateProvider { private readonly ILocalStorageService _localStorage; private readonly HttpClient _httpClient; private readonly AuthenticationState _anonymous; public JwtAuthenticationStateProvider(ILocalStorageService localStorage, HttpClient httpClient) { _localStorage = localStorage; _httpClient = httpClient; _anonymous = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } public override async Task GetAuthenticationStateAsync() { var token = await _localStorage.GetItemAsync("authToken"); if (string.IsNullOrWhiteSpace(token)) return _anonymous; var claims = ParseClaimsFromJwt(token); // Check expiration var expClaim = claims.FirstOrDefault(c => c.Type == "exp"); if (expClaim != null && long.TryParse(expClaim.Value, out long expTime)) { var expDateTime = DateTimeOffset.FromUnixTimeSeconds(expTime).UtcDateTime; if (expDateTime <= DateTime.UtcNow) { await _localStorage.RemoveItemAsync("authToken"); return _anonymous; } } _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token); return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"))); } public void NotifyUserAuthentication(string token) { var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(ParseClaimsFromJwt(token), "jwt")); var authState = Task.FromResult(new AuthenticationState(authenticatedUser)); NotifyAuthenticationStateChanged(authState); } public void NotifyUserLogout() { var authState = Task.FromResult(_anonymous); NotifyAuthenticationStateChanged(authState); } private IEnumerable ParseClaimsFromJwt(string jwt) { var claims = new List(); var payload = jwt.Split('.')[1]; var jsonBytes = ParseBase64WithoutPadding(payload); var keyValuePairs = JsonSerializer.Deserialize>(jsonBytes); if (keyValuePairs != null) { foreach (var kvp in keyValuePairs) { string claimType = kvp.Key; if (claimType == "role" || claimType == "Role") claimType = ClaimTypes.Role; else if (claimType == "unique_name" || claimType == "name") claimType = ClaimTypes.Name; if (kvp.Value is JsonElement element && element.ValueKind == JsonValueKind.Array) { foreach (var item in element.EnumerateArray()) { claims.Add(new Claim(claimType, item.ToString())); } } else { claims.Add(new Claim(claimType, kvp.Value.ToString() ?? "")); } } } return claims; } private byte[] ParseBase64WithoutPadding(string base64) { switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; } return Convert.FromBase64String(base64); } } }