alibaba/nacos · warning · AccessException

Token has expired

Error message

Token has expired

What it means

Thrown by validateClaims when the JWT 'exp' claim is absent or is in the past. The validator treats a missing expiration as expired (fails closed) and compares the present time against exp with no built-in clock-skew grace.

Source

Thrown at plugin-default-impl/nacos-oidc-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/oidc/token/JwtTokenValidator.java:206

            return claims;
            
        } catch (Exception e) {
            LOGGER.warn("Token validation failed even after JWKS refresh: {}", e.getMessage());
            throw new AccessException("Token signature verification failed");
        }
    }
    
    /**
     * Perform additional claims validation.
     *
     * @param claims JWT claims
     * @throws AccessException if validation fails
     */
    private void validateClaims(JWTClaimsSet claims) throws AccessException {
        // Validate expiration
        Date expirationTime = claims.getExpirationTime();
        if (expirationTime == null || expirationTime.before(new Date())) {
            throw new AccessException("Token has expired");
        }
        
        // Validate not before (if present)
        Date notBeforeTime = claims.getNotBeforeTime();
        if (notBeforeTime != null && notBeforeTime.after(new Date())) {
            throw new AccessException("Token is not yet valid");
        }
        
        // Validate audience (if client ID is configured)
        String clientId = config.getClientId();
        if (StringUtils.isNotBlank(clientId)) {
            List<String> audience = claims.getAudience();
            if (audience != null && !audience.isEmpty() && !audience.contains(clientId)) {
                // Check if 'azp' (authorized party) matches
                String azp = (String) claims.getClaim("azp");
                if (!clientId.equals(azp)) {
                    String message = String.format(
                        "Token audience mismatch. Expected: %s, Got: %s, azp: %s",

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Have the client refresh/renew the access token and resend.
  2. Synchronize clocks (NTP) on the Nacos server and the IdP to eliminate skew.
  3. If the IdP allows, increase the token TTL or implement a refresh-token flow client-side.
  4. Ensure the IdP includes an 'exp' claim in issued tokens.
  5. Confirm the server system time and timezone are correct.

Example fix

// before: token reused after expiry, no refresh
validator.validate(staleToken); // exp in the past

// after: refresh before reuse
if (tokenExpiredLocally(staleToken)) {
    staleToken = oidcClient.refresh(refreshToken);
}
validator.validate(staleToken);
Defensive patterns

Strategy: validation

Validate before calling

import java.util.Base64; import java.util.Date; import com.nimbusds.jwt.JWTClaimsSet;

JWTClaimsSet preview = JWTClaimsSet.parse(new String(Base64.getUrlDecoder().decode(token.split("\\.")[1])));
Date exp = preview.getExpirationTime();
if (exp == null || exp.before(new Date())) {
    // refresh token, do not call validate()
}

Try / catch

try {
    validator.validate(token);
} catch (AccessException e) {
    if ("Token has expired".equals(e.getMessage())) {
        // 401 token_expired; client must refresh
    }
    throw e;
}

Prevention

When it happens

Trigger: claims.getExpirationTime() returns null (no exp claim), or exp.before(new Date()) is true at validation time.

Common situations: Token genuinely expired (TTL elapsed); clock skew between the Nacos server and the IdP causes premature expiry; the IdP issues long-lived tokens but the client cached one past its exp; missing exp claim in a non-conformant token.

Related errors


AI-assisted analysis of alibaba/nacos@9b989acdf1 (2026-08-14). Data as JSON: /api/errors/e87bdc0adcadd4ae. Report an issue: GitHub.