using System.Net.Http.Json; using NexusReader.Application.Abstractions.Services; namespace NexusReader.UI.Shared.Services; public interface IIdentityService { Task RegisterAsync(string email, string password); Task LoginAsync(string email, string password, bool rememberMe = false); Task LogoutAsync(); Task GetProfileAsync(); Task RefreshTokenAsync(); } public record UserProfile( string Email, int AITokenLimit, int AITokensUsed, string CurrentPlan, Guid TenantId, int AverageQuizScore, string LastReadBookTitle); public class IdentityService : IIdentityService { private readonly HttpClient _httpClient; private readonly INativeStorageService _storageService; private readonly NexusAuthenticationStateProvider _authStateProvider; private const string TokenKey = "nexus_auth_token"; private const string RefreshTokenKey = "nexus_refresh_token"; public IdentityService( HttpClient httpClient, INativeStorageService storageService, NexusAuthenticationStateProvider authStateProvider) { _httpClient = httpClient; _storageService = storageService; _authStateProvider = authStateProvider; } public async Task RegisterAsync(string email, string password) { var response = await _httpClient.PostAsJsonAsync("identity/register", new { email, password }); return response.IsSuccessStatusCode; } public async Task LoginAsync(string email, string password, bool rememberMe = false) { var response = await _httpClient.PostAsJsonAsync("identity/login", new { email, password }); if (response.IsSuccessStatusCode) { var result = await response.Content.ReadFromJsonAsync(); if (result != null && !string.IsNullOrEmpty(result.AccessToken)) { await _storageService.SaveSecureString(TokenKey, result.AccessToken); if (!string.IsNullOrEmpty(result.RefreshToken)) { await _storageService.SaveSecureString(RefreshTokenKey, result.RefreshToken); } // Option A: Fetch profile to get claims var profile = await GetProfileAsync(); if (profile != null) { await _storageService.SaveSecureString("nexus_user_email", profile.Email); await _storageService.SaveSecureString("nexus_user_tenant", profile.TenantId.ToString()); _authStateProvider.NotifyUserAuthentication(profile.Email, profile.TenantId.ToString()); } else { // Fallback if profile fetch fails _authStateProvider.NotifyUserAuthentication(email, "unknown"); } return true; } } return false; } public async Task LogoutAsync() { _storageService.RemoveSecure(TokenKey); _storageService.RemoveSecure(RefreshTokenKey); _storageService.RemoveSecure("nexus_user_email"); _storageService.RemoveSecure("nexus_user_tenant"); _authStateProvider.NotifyUserLogout(); } public async Task GetProfileAsync() { try { return await _httpClient.GetFromJsonAsync("identity/profile"); } catch { return null; } } public async Task RefreshTokenAsync() { var result = await _storageService.GetSecureString(RefreshTokenKey); var refreshToken = result.IsSuccess ? result.Value : null; if (string.IsNullOrEmpty(refreshToken)) return false; var response = await _httpClient.PostAsJsonAsync("identity/refresh", new { refreshToken }); if (response.IsSuccessStatusCode) { var loginResult = await response.Content.ReadFromJsonAsync(); if (loginResult != null && !string.IsNullOrEmpty(loginResult.AccessToken)) { await _storageService.SaveSecureString(TokenKey, loginResult.AccessToken); if (!string.IsNullOrEmpty(loginResult.RefreshToken)) { await _storageService.SaveSecureString(RefreshTokenKey, loginResult.RefreshToken); } var profile = await GetProfileAsync(); if (profile != null) { await _storageService.SaveSecureString("nexus_user_email", profile.Email); await _storageService.SaveSecureString("nexus_user_tenant", profile.TenantId.ToString()); _authStateProvider.NotifyUserAuthentication(profile.Email, profile.TenantId.ToString()); } return true; } } return false; } private class LoginResponse { public string TokenType { get; set; } = string.Empty; public string AccessToken { get; set; } = string.Empty; public int ExpiresIn { get; set; } public string RefreshToken { get; set; } = string.Empty; } }