using Microsoft.AspNetCore.Components.Authorization; using System.IdentityModel.Tokens.Jwt; using System.Net.Http.Headers; using System.Security.Claims; namespace PlaylistShared.Pwa.Services; public class AuthStateProvider : AuthenticationStateProvider, IDisposable { private readonly TokenStorage _tokenStorage; private readonly ApiClient _apiClient; private readonly HttpClient _http; private Timer? _refreshTimer; private ClaimsPrincipal _currentUser = new(new ClaimsIdentity()); public AuthStateProvider(TokenStorage tokenStorage, ApiClient apiClient, HttpClient http) { _tokenStorage = tokenStorage; _apiClient = apiClient; _http = http; } public override async Task GetAuthenticationStateAsync() { var (token, refreshToken) = await _tokenStorage.GetTokensAsync(); if (string.IsNullOrEmpty(token)) return new AuthenticationState(_currentUser); var principal = ParseToken(token); if (principal == null) return new AuthenticationState(_currentUser); _currentUser = principal; _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); ScheduleTokenRefresh(token, refreshToken); return new AuthenticationState(principal); } public async Task MarkUserAsAuthenticated(string token, string refreshToken) { await _tokenStorage.SetTokensAsync(token, refreshToken); var principal = ParseToken(token); _currentUser = principal ?? new ClaimsPrincipal(new ClaimsIdentity()); _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } public async Task MarkUserAsLoggedOut() { await _tokenStorage.ClearTokensAsync(); _currentUser = new ClaimsPrincipal(new ClaimsIdentity()); _http.DefaultRequestHeaders.Authorization = null; NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } private ClaimsPrincipal? ParseToken(string token) { try { var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(token); var identity = new ClaimsIdentity(jwt.Claims, "jwt"); return new ClaimsPrincipal(identity); } catch (Exception ex) { return null; } } private void ScheduleTokenRefresh(string token, string? refreshToken) { var handler = new JwtSecurityTokenHandler(); var jwt = handler.ReadJwtToken(token); var expiresAt = jwt.ValidTo; var timeToExpiry = expiresAt - DateTime.UtcNow; var refreshTime = timeToExpiry - TimeSpan.FromMinutes(5); if (refreshTime > TimeSpan.Zero && !string.IsNullOrEmpty(refreshToken)) { _refreshTimer?.Dispose(); _refreshTimer = new Timer(async _ => { try { var newToken = await _apiClient.RefreshTokenAsync(refreshToken); if (newToken != null) await MarkUserAsAuthenticated(newToken.Token, newToken.RefreshToken); else await MarkUserAsLoggedOut(); } catch { await MarkUserAsLoggedOut(); } }, null, (int)refreshTime.TotalMilliseconds, Timeout.Infinite); } } public void Dispose() => _refreshTimer?.Dispose(); }