using CPRNIMS.Infrastructure.Dto.Account; using CPRNIMS.Infrastructure.Models.Account; using CPRNIMS.Infrastructure.ViewModel.Account; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using System.IdentityModel.Tokens.Jwt; using System.Net.Http.Headers; using System.Net.Http.Json; using System.Security.Claims; using System.Text.Json; namespace CPRNIMS.Infrastructure.Helper { public class TokenHelper { private readonly IHttpClientFactory _httpClientFactory; private readonly IConfiguration _configuration; private readonly IHttpContextAccessor _httpContextAccessor; public TokenHelper( IHttpClientFactory httpClientFactory, IConfiguration configuration, IHttpContextAccessor httpContextAccessor) { _httpClientFactory = httpClientFactory; _configuration = configuration; _httpContextAccessor = httpContextAccessor; } public async Task LoginAsync(LoginVM loginModel) { var loginResponse = new LoginResponse(); try { var httpClient = _httpClientFactory.CreateClient("AuthApi"); var response = await httpClient.PostAsJsonAsync( _configuration["Account:Login"], loginModel); loginResponse = JsonSerializer.Deserialize( await response.Content.ReadAsStringAsync()); if (response.IsSuccessStatusCode && loginResponse != null) { return loginResponse; } else { var errorContent = await response.Content.ReadAsStringAsync(); loginResponse.message = errorContent; return loginResponse; } } catch (Exception ex) { loginResponse.message = ex.Message; return loginResponse; } } public async Task GetValidTokenAsync() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext?.User?.Identity?.IsAuthenticated != true) return null; // Get token from claims var tokenClaim = httpContext.User.FindFirst("Token"); var expiryStr = httpContext.User.FindFirst("TokenExpiry")?.Value; var refreshTokenClaim = httpContext.User.FindFirst("RefreshToken"); if (tokenClaim == null || string.IsNullOrEmpty(tokenClaim.Value)) return null; // Check if token is expiring soon if (!string.IsNullOrEmpty(expiryStr) && DateTime.TryParse(expiryStr, out DateTime expiry)) { // If token expires in less than 5 minutes, refresh it if (DateTime.UtcNow.AddMinutes(5) >= expiry) { if (refreshTokenClaim != null && !string.IsNullOrEmpty(refreshTokenClaim.Value)) { var newTokenInfo = await RefreshTokenAsync(refreshTokenClaim.Value); if (newTokenInfo != null) { // Update claims with new token await UpdateTokenInClaims(newTokenInfo); return newTokenInfo.AccessToken; } return null; // Refresh failed } } } return tokenClaim.Value; } private async Task UpdateTokenInClaims(TokenInfo tokenInfo) { var httpContext = _httpContextAccessor.HttpContext; var currentPrincipal = httpContext.User; // Create new claims list with updated token var claims = currentPrincipal.Claims.Where(c => c.Type != "Token" && c.Type != "TokenExpiry" && c.Type != "RefreshToken").ToList(); claims.Add(new Claim("Token", tokenInfo.AccessToken)); claims.Add(new Claim("TokenExpiry", tokenInfo.ExpiresAt.ToString("O"))); if (!string.IsNullOrEmpty(tokenInfo.RefreshToken)) claims.Add(new Claim("RefreshToken", tokenInfo.RefreshToken)); var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); await httpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddHours(2), AllowRefresh = true }); } private async Task RefreshTokenAsync(string refreshToken) { try { var httpClient = _httpClientFactory.CreateClient("AuthApi"); var response = await httpClient.PostAsJsonAsync( _configuration["Account:Refresh"], new { refreshToken }); if (response.IsSuccessStatusCode) { var loginResponse = JsonSerializer.Deserialize( await response.Content.ReadAsStringAsync()); var expiresAt = CalculateExpiration(loginResponse); return new TokenInfo { AccessToken = loginResponse.token, RefreshToken = loginResponse.refreshToken, ExpiresAt = expiresAt, IssuedAt = DateTime.UtcNow, Claims = ExtractClaimsFromToken(loginResponse.token) }; } } catch (Exception) { // Refresh failed } return null; } private DateTime CalculateExpiration(LoginResponse response) { // Try multiple sources for expiration if (response.expiresInSeconds > 0) { return DateTime.UtcNow.AddSeconds(response.expiresInSeconds); } else if (response.expiresAt > DateTime.MinValue && response.expiresAt.Year > 1) { return response.expiresAt; } else if (!string.IsNullOrEmpty(response.token)) { var expiry = ExtractExpirationFromToken(response.token); if (expiry > DateTime.MinValue) return expiry; } // Default: 2 hours return DateTime.UtcNow.AddHours(2); } private DateTime ExtractExpirationFromToken(string token) { try { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); if (jwtToken.ValidTo != DateTime.MinValue && jwtToken.ValidTo.Year > 1) { return jwtToken.ValidTo; } // Check exp claim var expClaim = jwtToken.Claims.FirstOrDefault(c => c.Type == "exp"); if (expClaim != null && long.TryParse(expClaim.Value, out long exp)) { return DateTimeOffset.FromUnixTimeSeconds(exp).UtcDateTime; } } catch { // Token parsing failed } return DateTime.MinValue; } private Dictionary ExtractClaimsFromToken(string token) { var claims = new Dictionary(); try { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadJwtToken(token); foreach (var claim in jwtToken.Claims) { if (!claims.ContainsKey(claim.Type)) { claims[claim.Type] = claim.Value; } } } catch (Exception) { // Token parsing failed } return claims; } public Dictionary GetStoredClaims() { var httpContext = _httpContextAccessor.HttpContext; if (httpContext?.User?.Identity?.IsAuthenticated != true) return new Dictionary(); var tokenClaim = httpContext.User.FindFirst("Token"); if (tokenClaim == null || string.IsNullOrEmpty(tokenClaim.Value)) return new Dictionary(); return ExtractClaimsFromToken(tokenClaim.Value); } // Rest of your existing methods... public HttpClient CreateHttpClientWithDefaultHeaders(string token) { string BaseUrl = _configuration["CommonEndpoints:ApiDefaultHeaders:BaseUrl"]; var httpClient = new HttpClient(new HttpClientHandler { ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true }) { BaseAddress = new Uri(BaseUrl) }; httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); var customHeaders = CustomHeaders; foreach (var header in customHeaders) { httpClient.DefaultRequestHeaders.Add(header.Key, header.Value); } return httpClient; } public Dictionary DefaultHeaders { get { var headersSection = _configuration.GetSection( "CommonEndpoints:ApiDefaultHeaders"); var headers = new Dictionary(); foreach (var childSection in headersSection.GetChildren()) { headers[childSection.Key] = childSection.Value; } return headers; } } public Dictionary CustomHeaders { get { var headersSection = _configuration.GetSection( "CommonEndpoints:CustomApiHeaders"); var headers = new Dictionary(); foreach (var childSection in headersSection.GetChildren()) { headers[childSection.Key] = childSection.Value; } return headers; } } } }