using Blazored.LocalStorage; using FrontOffice.BFF.User.Protobuf.Protos.User; using Microsoft.AspNetCore.Components; using MudBlazor; using System.IdentityModel.Tokens.Jwt; namespace FrontOffice.Main.Utilities; public class AuthService { private readonly ILocalStorageService _localStorage; private readonly NavigationManager _navigation; private readonly ISnackbar _snackbar; private readonly UserAuthInfo _userAuthInfo; private readonly UserContract.UserContractClient _userContract; private readonly ILogger _logger; private const string TokenStorageKey = "auth:token"; public AuthService( ILocalStorageService localStorage, NavigationManager navigation, ISnackbar snackbar, UserAuthInfo userAuthInfo, UserContract.UserContractClient userContract, ILogger logger) { _localStorage = localStorage; _navigation = navigation; _snackbar = snackbar; _userAuthInfo = userAuthInfo; _userContract = userContract; _logger = logger; } public async Task IsAuthenticatedAsync() { var token = await _localStorage.GetItemAsync(TokenStorageKey); return !string.IsNullOrWhiteSpace(token); } public async Task GetTokenAsync() { return await _localStorage.GetItemAsync(TokenStorageKey); } public async Task IsCompleteRegisterAsync() { await InitUserAuthInfo(); if (!string.IsNullOrWhiteSpace(_userAuthInfo.NationalCode) && !string.IsNullOrWhiteSpace(_userAuthInfo.FirstName) && !string.IsNullOrWhiteSpace(_userAuthInfo.LastName) && _userAuthInfo.IsSignMainContract) { return true; } return false; } public bool IsCompleteRegister() { InitUserAuthInfo().GetAwaiter(); if (!string.IsNullOrWhiteSpace(_userAuthInfo.NationalCode) && !string.IsNullOrWhiteSpace(_userAuthInfo.FirstName) && !string.IsNullOrWhiteSpace(_userAuthInfo.LastName) && _userAuthInfo.IsSignMainContract) { return true; } return false; } public async Task GetUserAuthInfo() { await InitUserAuthInfo(); return _userAuthInfo; } public async Task InitUserAuthInfo() { var claims = await GetTokenClaimsAsync(); _userAuthInfo.UserClaims= claims; var userIdStr = claims.FirstOrDefault(c => c.key == "UserId" || c.key == "sub" || c.key == "nameid").value; var firstName = claims.FirstOrDefault(c => c.key == "FirstName").value; var lastName = claims.FirstOrDefault(c => c.key == "LastName").value; var nationalCode = claims.FirstOrDefault(c => c.key == "NationalCode").value; var mobileNumber = claims.FirstOrDefault(c => c.key == "MobileNumber").value; var isSignMainContractStr = claims.FirstOrDefault(c => c.key == "IsSignMainContract").value; var hasPurchasedPackageStr = claims.FirstOrDefault(c => c.key == "HasPurchasedPackage").value; var isClubMemberActiveStr = claims.FirstOrDefault(c => c.key == "IsClubMemberActive").value; _userAuthInfo.UserId = long.TryParse(userIdStr, out var userId) ? userId : 0; _userAuthInfo.FirstName = firstName ?? string.Empty; _userAuthInfo.LastName = lastName ?? string.Empty; _userAuthInfo.NationalCode = nationalCode ?? string.Empty; _userAuthInfo.MobileNumber = mobileNumber ?? string.Empty; _userAuthInfo.IsSignMainContract = bool.TryParse(isSignMainContractStr, out var isSignMainContract) && isSignMainContract; _userAuthInfo.HasPurchasedPackage = bool.TryParse(hasPurchasedPackageStr, out var hasPurchased) && hasPurchased; _userAuthInfo.IsClubMemberActive = bool.TryParse(isClubMemberActiveStr, out var isClubActive) && isClubActive; } private async Task?> GetTokenClaimsAsync() { var token = await GetTokenAsync(); if (string.IsNullOrEmpty(token)) return null; var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); return jwtToken.Claims.Select(c => (c.Type, c.Value)).ToList(); } public async Task LogoutAsync() { await _localStorage.RemoveItemAsync(TokenStorageKey); await _localStorage.RemoveItemAsync("auth:phone-number"); await _localStorage.RemoveItemAsync("auth:redirect"); await _localStorage.RemoveItemAsync("referral:code"); _snackbar.Add("با موفقیت از حساب کاربری خارج شدید.", Severity.Success); _navigation.NavigateTo(RouteConstants.Main.MainPage); } /// /// Refresh the user's token by calling the BFF RefreshToken API. /// If successful, the new token is stored and user info is updated. /// /// True if token was refreshed successfully public async Task RefreshTokenAsync() { try { var currentToken = await GetTokenAsync(); if (string.IsNullOrEmpty(currentToken)) { _logger.LogWarning("No token found for refresh"); return false; } var response = await _userContract.RefreshTokenAsync(new RefreshTokenRequest { CurrentToken = currentToken }); if (response.Success && !string.IsNullOrEmpty(response.Token)) { // Store the new token await _localStorage.SetItemAsync(TokenStorageKey, response.Token); // Update user auth info with new token claims await InitUserAuthInfo(); _logger.LogInformation("Token refreshed successfully"); return true; } else { _logger.LogInformation("Token refresh not needed: {Message}", response.Message); return false; } } catch (Exception ex) { _logger.LogError(ex, "Failed to refresh token"); return false; } } public async Task RequireAuthenticationAsync() { var isAuthenticated = await IsAuthenticatedAsync(); if (!isAuthenticated) { _snackbar.Add("لطفاً ابتدا وارد حساب کاربری شوید.", Severity.Warning); _navigation.NavigateTo(RouteConstants.Main.MainPage); } } }