diff --git a/src/BackOffice/Common/Utilities/AppTokenProvider.cs b/src/BackOffice/Common/Utilities/AppTokenProvider.cs index 6abbf8e..dc40351 100644 --- a/src/BackOffice/Common/Utilities/AppTokenProvider.cs +++ b/src/BackOffice/Common/Utilities/AppTokenProvider.cs @@ -1,4 +1,5 @@ -using BackOffice.Common.Utilities; +using System.IdentityModel.Tokens.Jwt; +using BackOffice.Common.Utilities; using Blazored.LocalStorage; namespace BackOffice.Common.Utilities; @@ -19,9 +20,41 @@ public class AppTokenProvider : ITokenProvider { var authorizationToken = await _localStorage.GetItemAsync(GlobalConstants.JwtTokenKey); if (!string.IsNullOrEmpty(authorizationToken)) - _token = authorizationToken.ToString().Replace("Bearer ", ""); + { + var raw = authorizationToken.ToString().Replace("Bearer ", ""); + // Don't send expired tokens — they cause 401 on anonymous endpoints like login + if (IsTokenExpired(raw)) + { + await _localStorage.RemoveItemAsync(GlobalConstants.JwtTokenKey); + _token = null; + return _token; + } + _token = raw; + } + } + else if (IsTokenExpired(_token)) + { + // Cached token has expired since last check + await _localStorage.RemoveItemAsync(GlobalConstants.JwtTokenKey); + _token = null; } return _token; } + + private static bool IsTokenExpired(string token) + { + try + { + var handler = new JwtSecurityTokenHandler(); + var jwt = handler.ReadJwtToken(token); + // Add 30-second buffer to avoid edge-case race + return jwt.ValidTo < DateTime.UtcNow.AddSeconds(-30); + } + catch + { + // Malformed token — treat as expired + return true; + } + } } \ No newline at end of file