using System.Security.Claims; using System.Text.Json; using Microsoft.AspNetCore.Components.Authorization; using NexusReader.Application.Abstractions.Services; using NexusReader.UI.Shared.Constants; namespace NexusReader.UI.Shared.Services; public class NexusAuthenticationStateProvider : AuthenticationStateProvider { private readonly INativeStorageService _storageService; private const string TokenKey = StorageKeys.AuthToken; public NexusAuthenticationStateProvider(INativeStorageService storageService) { _storageService = storageService; } public void ClearCache() { _cachedState = null; NotifyAuthenticationStateChanged(GetAuthenticationStateAsync()); } private AuthenticationState? _cachedState; public override async Task GetAuthenticationStateAsync() { try { if (_cachedState != null) return _cachedState; var tokenResult = await _storageService.GetSecureString(TokenKey); var token = tokenResult.IsSuccess ? tokenResult.Value : null; // 1. Try Token-based auth if (!string.IsNullOrWhiteSpace(token)) { var emailResult = await _storageService.GetSecureString(StorageKeys.UserEmail); var tenantIdResult = await _storageService.GetSecureString(StorageKeys.UserTenant); if (emailResult.IsSuccess && !string.IsNullOrEmpty(emailResult.Value)) { _cachedState = CreateState(emailResult.Value, tenantIdResult.IsSuccess ? tenantIdResult.Value! : "unknown", "OpaqueBearer"); return _cachedState; } } // 2. Try Cookie-based auth indicators var storedEmailResult = await _storageService.GetSecureString(StorageKeys.UserEmail); if (storedEmailResult.IsSuccess && !string.IsNullOrEmpty(storedEmailResult.Value)) { var tenantIdResult = await _storageService.GetSecureString(StorageKeys.UserTenant); _cachedState = CreateState(storedEmailResult.Value, tenantIdResult.IsSuccess ? tenantIdResult.Value! : "unknown", "CookieAuth"); return _cachedState; } // 3. Fallback: If we have no local info, we might still have a cookie (e.g. after refresh or Google login). // We should return anonymous for now but trigger a background check if we're in WASM. // Wait! In WASM, the first GetAuthenticationStateAsync is awaited. // We can do a quick check here if it's the first time. return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } catch (Exception) { return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); } } private AuthenticationState CreateState(string email, string tenantId, string authType) { var claims = new List { new Claim(ClaimTypes.Name, email), new Claim(ClaimTypes.Email, email), new Claim("TenantId", tenantId) }; var identity = new ClaimsIdentity(claims, authType); return new AuthenticationState(new ClaimsPrincipal(identity)); } public void NotifyUserAuthentication(string email, string tenantId) { _cachedState = CreateState(email, tenantId, "OpaqueBearer"); NotifyAuthenticationStateChanged(Task.FromResult(_cachedState)); } public void NotifyUserLogout() { _cachedState = null; var guest = new ClaimsPrincipal(new ClaimsIdentity()); NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(guest))); } }