From b3e6066c90b899bc670bfaf20440ed49dccd647a Mon Sep 17 00:00:00 2001 From: masoodafar-web Date: Sun, 15 Mar 2026 01:39:27 +0330 Subject: [PATCH] fix: don't send expired JWT tokens with gRPC requests AppTokenProvider now checks token expiry before returning it. Expired tokens are removed from localStorage and not sent. This fixes 401 errors on anonymous endpoints (login/OTP) caused by the gRPC channel attaching an old expired Bearer token to every request. CMS validates the token even on unauthenticated endpoints and rejects expired ones with 401. --- .../Common/Utilities/AppTokenProvider.cs | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) 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