using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using System.Text; using System.Text.Json; using System.Threading.Tasks; using Auth.Contracts.DTOs.Auth; using Blazored.LocalStorage; using Microsoft.AspNetCore.Components.Authorization; namespace Auth.RCL.Services { public class ApiAuthenticationStateProvider : AuthenticationStateProvider { private readonly HttpClient _httpClient; private readonly ILocalStorageService _localStorage; public ApiAuthenticationStateProvider( HttpClient httpClient, ILocalStorageService localStorage ) { _httpClient = httpClient; _localStorage = localStorage; } public override async Task GetAuthenticationStateAsync() { var refreshToken = await _localStorage.GetItemAsync("refreshToken"); var accessToken = await _localStorage.GetItemAsync("authToken"); var expiryDate = await _localStorage.GetItemAsync("expiryDate"); if (string.IsNullOrWhiteSpace(accessToken)) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); if (DateTime.Now.AddMinutes(4) > DateTime.Parse(expiryDate)) { var tokenModel = JsonSerializer.Serialize( new TokenModel() { RefreshToken = refreshToken, AccessToken = accessToken, Expiry = new DateTime(), } ); var response = await _httpClient.PostAsync( "auth/refresh", new StringContent(tokenModel, Encoding.UTF8, "application/json") ); if (!response.IsSuccessStatusCode) { _httpClient.DefaultRequestHeaders.Authorization = null; MarkUserAsLoggedOut(); return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } var loginResult = JsonSerializer.Deserialize( await response.Content.ReadAsStringAsync(), new JsonSerializerOptions { PropertyNameCaseInsensitive = true } ); await _localStorage.SetItemAsync("authToken", loginResult.AccessToken); await _localStorage.SetItemAsync("refreshToken", loginResult.RefreshToken); await _localStorage.SetItemAsync("expiryDate", loginResult.Expiry); accessToken = loginResult.AccessToken; } // if (string.IsNullOrWhiteSpace(savedToken) || DateTime.Now > DateTime.Parse(expiryDate)) // { // return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); // } _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( "Bearer", accessToken ); var claims = ParseClaimsFromJwt(accessToken); if (claims is null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt"))); } public async void MarkUserAsAuthenticated(string jwtToken) { var claims = ParseClaimsFromJwt(jwtToken); var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth")); var authState = Task.FromResult(new AuthenticationState(authenticatedUser)); NotifyAuthenticationStateChanged(authState); // var userId = authenticatedUser.Claims.FirstOrDefault(x => x.Type == "uid").Value; } public async Task MarkUserAsLoggedOut() { var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity()); var authState = Task.FromResult(new AuthenticationState(anonymousUser)); NotifyAuthenticationStateChanged(authState); _httpClient.DefaultRequestHeaders.Authorization = null; } private IEnumerable ParseClaimsFromJwt(string jwt) { try { var claims = new List(); var payload = jwt.Split('.')[1]; var jsonBytes = ParseBase64WithoutPadding(payload); var keyValuePairs = JsonSerializer.Deserialize>( jsonBytes ); keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles); if (roles != null) { if (roles.ToString().Trim().StartsWith("[")) { var parsedRoles = JsonSerializer.Deserialize(roles.ToString()); foreach (var parsedRole in parsedRoles) { claims.Add(new Claim(ClaimTypes.Role, parsedRole)); } } else { claims.Add(new Claim(ClaimTypes.Role, roles.ToString())); } keyValuePairs.Remove(ClaimTypes.Role); } claims.AddRange( keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString())) ); return claims; } catch { return null; } } private byte[] ParseBase64WithoutPadding(string base64) { switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; } return Convert.FromBase64String(base64); } } }