quarkusio/quarkus · warning · InvalidJwtException
EXPIRED
EXPIRED
Error message
Logout token issued to client %s expired %d seconds ago
What it means
Back-channel logout tokens (JWTs sent by the OIDC provider on logout) are validated including expiration. This InvalidJwtException with ErrorCodes.EXPIRED is thrown when the logout token's 'exp' claim plus the configured lifespan grace has passed, reporting how many seconds ago it expired.
Source
Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java:210
asymmetricKeyResolver, enforceExpReq, oidcConfig.token().issuedAtRequired());
}
public TokenVerificationResult verifyLogoutJwtToken(String token) throws InvalidJwtException {
final boolean enforceExpReq = !oidcConfig.token().age().isPresent();
TokenVerificationResult result = verifyJwtTokenInternal(token, true, false, null, ASYMMETRIC_ALGORITHM_CONSTRAINTS,
asymmetricKeyResolver, enforceExpReq, oidcConfig.token().issuedAtRequired());
if (!enforceExpReq) {
// Expiry check was skipped during the initial verification but if the logout token contains the exp claim
// then it must be verified
final Long exp = result.localVerificationResult().getLong(Claims.exp.name());
if (exp != null) {
final long secondsAfterExpiry = now() / 1000 - (exp + getLifespanGrace());
if (secondsAfterExpiry > 0) {
String error = "Logout token issued to client %s expired %d seconds ago".formatted(
oidcConfig.clientId().get(),
secondsAfterExpiry);
LOG.warn(error);
throw new InvalidJwtException(error, List.of(new ErrorCodeValidator.Error(ErrorCodes.EXPIRED, error)),
null);
}
}
}
return result;
}
private TokenVerificationResult verifyJwtTokenInternal(String token,
boolean enforceAudienceVerification,
boolean subjectRequired,
String nonce,
AlgorithmConstraints algConstraints,
VerificationKeyResolver verificationKeyResolver, boolean enforceExpReq, boolean issuedAtRequired)
throws InvalidJwtException {
JwtConsumerBuilder builder = new JwtConsumerBuilder();
builder.setVerificationKeyResolver(verificationKeyResolver);
View on GitHub (pinned to e1c734241f)
Solutions
- Increase quarkus.oidc.token.lifespan-grace to tolerate the observed delay/skew.
- Synchronize clocks (NTP) between the IdP and the Quarkus application hosts.
- If the provider retries stale logout tokens, ensure the app acknowledges/flushes the logout queue promptly.
- Verify with the provider why logout tokens are generated so far in the past.
Example fix
// before # (default lifespan grace) // after quarkus.oidc.token.lifespan-grace=60
Defensive patterns
Strategy: try-catch
Validate before calling
Long exp = logoutToken.getLongClaimValue("exp");
if (exp != null && System.currentTimeMillis() / 1000 > exp + graceSeconds) {
// skip processing; token already expired
} Type guard
boolean isLogoutTokenFresh(Claims claims, long grace) {
return claims.getExpirationTime() == null
|| System.currentTimeMillis() / 1000 <= claims.getExpirationTime() + grace;
} Try / catch
try {
provider.verifyLogoutJwtToken(token);
} catch (InvalidJwtException e) {
if (e.hasError(ErrorCodes.EXPIRED)) { LOG.info("Stale logout token ignored"); return; }
throw e;
} Prevention
- Set quarkus.oidc.token.lifespan-grace to cover processing delays and retries
- Run NTP time sync on both IdP and application hosts
- Process logout callbacks promptly so queued tokens don't age past exp
When it happens
Trigger: verifyLogoutJwtToken receives a logout token whose exp < now - getLifespanGrace(); often caused by provider retries of old logout events or large clock skew between IdP and app.
Common situations: Keycloak admin revoking sessions causing replay of stale logout tokens; app downtime delaying processing of queued logout tokens; clock drift between containers.
Related errors
- Failed to generate key id
- Application 'web-app' type is only supported if access token
- Failed to parse the realm name.
- Failed to find a matching OidcTenantConfig for tenant:
- %s type can not be used to represent JWT claims in @Singleto
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/53508f7fb2e3fa1b.
Report an issue: GitHub.