Spaces:
Sleeping
Sleeping
| 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<AuthenticationState> GetAuthenticationStateAsync() | |
| { | |
| var token = await _localStorage.GetItemAsync<string>("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<Claim> ParseClaimsFromJwt(string jwt) | |
| { | |
| var claims = new List<Claim>(); | |
| var payload = jwt.Split('.')[1]; | |
| var jsonBytes = ParseBase64WithoutPadding(payload); | |
| var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(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); | |
| } | |
| } | |
| } | |