quarkusio/quarkus · error · InvalidJwtException

ISSUED_AT_INVALID_PAST

ISSUED_AT_INVALID_PAST

Error message

Token age exceeds the configured token age property

What it means

When quarkus.oidc.token.age is configured, Quarkus checks the 'iat' (issued-at) claim of verified JWTs: if now - iat exceeds the configured max age plus lifespan grace, an InvalidJwtException with ErrorCodes.ISSUED_AT_INVALID_PAST is thrown. This enforces a maximum token age regardless of the token's own exp.

Source

Thrown at extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/OidcProvider.java:342

                String newHeaders = new String(
                        Base64.getUrlEncoder().withoutPadding().encode(headers.toString().getBytes()),
                        StandardCharsets.UTF_8);
                int dotIndex = token.indexOf('.');
                String newToken = newHeaders + token.substring(dotIndex);
                return newToken;
            }
        }
        return token;
    }

    private void verifyTokenAge(Long iat) throws InvalidJwtException {
        if (oidcConfig.token().age().isPresent() && iat != null) {
            final long now = now() / 1000;

            if (now - iat > oidcConfig.token().age().get().toSeconds() + getLifespanGrace()) {
                final String errorMessage = "Token age exceeds the configured token age property";
                LOG.warn(errorMessage);
                throw new InvalidJwtException(errorMessage,
                        List.of(new ErrorCodeValidator.Error(ErrorCodes.ISSUED_AT_INVALID_PAST, errorMessage)), null);
            }
        }
    }

    public Uni<TokenVerificationResult> refreshJwksAndVerifyJwtToken(String token, boolean enforceAudienceVerification,
            boolean subjectRequired, String nonce) {
        return asymmetricKeyResolver.refresh().onItem()
                .transformToUni(new Function<Void, Uni<? extends TokenVerificationResult>>() {

                    @Override
                    public Uni<? extends TokenVerificationResult> apply(Void v) {
                        try {
                            return Uni.createFrom()
                                    .item(verifyJwtToken(token, enforceAudienceVerification, subjectRequired, nonce));
                        } catch (Throwable t) {
                            return Uni.createFrom().failure(t);
                        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Increase quarkus.oidc.token.age to a value >= the IdP's access token lifetime.
  2. Remove quarkus.oidc.token.age if only exp-based expiry is desired.
  3. Increase quarkus.oidc.token.lifespan-grace to absorb clock skew.
  4. Synchronize clocks between IdP and application hosts.

Example fix

// before
quarkus.oidc.token.age=1M
// after
quarkus.oidc.token.age=1H
Defensive patterns

Strategy: validation

Validate before calling

Long iat = jwt.getClaims().getIssuedAt();
long maxAgeSec = config.token().age().get().toSeconds();
if (iat != null && System.currentTimeMillis() / 1000 - iat > maxAgeSec + grace) {
    // reject before calling the provider
}

Type guard

boolean isWithinConfiguredAge(Claims claims, Duration maxAge, long grace) {
    return claims.getIssuedAt() == null
        || System.currentTimeMillis() / 1000 - claims.getIssuedAt() <= maxAge.toSeconds() + grace;
}

Try / catch

try {
    return provider.verifyJwtToken(token, enforceAudience);
} catch (InvalidJwtException e) {
    if (e.hasError(ErrorCodes.ISSUED_AT_INVALID_PAST)) { /* refresh token */ }
    throw e;
}

Prevention

When it happens

Trigger: A JWT whose iat is older than quarkus.oidc.token.age (e.g. long-lived tokens or tokens with missing/wrong iat), verified by verifyJwtTokenInternal or introspection age check.

Common situations: Setting a very small token age (e.g. 1m) while the IdP issues tokens with lifetimes of hours; clock skew making iat appear older; test tokens with hardcoded old iat values.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/42683f6076ee8939. Report an issue: GitHub.