fix: don't send expired JWT tokens with gRPC requests
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 15m19s

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.
This commit is contained in:
masoodafar-web
2026-03-15 01:39:27 +03:30
parent 148b8e4011
commit b3e6066c90
@@ -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<string>(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;
}
}
}