{"record":{"id":"e87bdc0adcadd4ae","repo":"alibaba/nacos","slug":"token-has-expired","errorCode":null,"errorMessage":"Token has expired","messagePattern":"Token has expired","errorType":"exception","errorClass":"AccessException","httpStatus":null,"severity":"warning","filePath":"plugin-default-impl/nacos-oidc-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/oidc/token/JwtTokenValidator.java","lineNumber":206,"sourceCode":"            return claims;\n            \n        } catch (Exception e) {\n            LOGGER.warn(\"Token validation failed even after JWKS refresh: {}\", e.getMessage());\n            throw new AccessException(\"Token signature verification failed\");\n        }\n    }\n    \n    /**\n     * Perform additional claims validation.\n     *\n     * @param claims JWT claims\n     * @throws AccessException if validation fails\n     */\n    private void validateClaims(JWTClaimsSet claims) throws AccessException {\n        // Validate expiration\n        Date expirationTime = claims.getExpirationTime();\n        if (expirationTime == null || expirationTime.before(new Date())) {\n            throw new AccessException(\"Token has expired\");\n        }\n        \n        // Validate not before (if present)\n        Date notBeforeTime = claims.getNotBeforeTime();\n        if (notBeforeTime != null && notBeforeTime.after(new Date())) {\n            throw new AccessException(\"Token is not yet valid\");\n        }\n        \n        // Validate audience (if client ID is configured)\n        String clientId = config.getClientId();\n        if (StringUtils.isNotBlank(clientId)) {\n            List<String> audience = claims.getAudience();\n            if (audience != null && !audience.isEmpty() && !audience.contains(clientId)) {\n                // Check if 'azp' (authorized party) matches\n                String azp = (String) claims.getClaim(\"azp\");\n                if (!clientId.equals(azp)) {\n                    String message = String.format(\n                        \"Token audience mismatch. Expected: %s, Got: %s, azp: %s\",","sourceCodeStart":188,"sourceCodeEnd":224,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/plugin-default-impl/nacos-oidc-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/oidc/token/JwtTokenValidator.java#L188-L224","documentation":"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.","triggerScenarios":"claims.getExpirationTime() returns null (no exp claim), or exp.before(new Date()) is true at validation time.","commonSituations":"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.","solutions":["Have the client refresh/renew the access token and resend.","Synchronize clocks (NTP) on the Nacos server and the IdP to eliminate skew.","If the IdP allows, increase the token TTL or implement a refresh-token flow client-side.","Ensure the IdP includes an 'exp' claim in issued tokens.","Confirm the server system time and timezone are correct."],"exampleFix":"// before: token reused after expiry, no refresh\nvalidator.validate(staleToken); // exp in the past\n\n// after: refresh before reuse\nif (tokenExpiredLocally(staleToken)) {\n    staleToken = oidcClient.refresh(refreshToken);\n}\nvalidator.validate(staleToken);","handlingStrategy":"validation","validationCode":"import java.util.Base64; import java.util.Date; import com.nimbusds.jwt.JWTClaimsSet;\n\nJWTClaimsSet preview = JWTClaimsSet.parse(new String(Base64.getUrlDecoder().decode(token.split(\"\\\\.\")[1])));\nDate exp = preview.getExpirationTime();\nif (exp == null || exp.before(new Date())) {\n    // refresh token, do not call validate()\n}","typeGuard":null,"tryCatchPattern":"try {\n    validator.validate(token);\n} catch (AccessException e) {\n    if (\"Token has expired\".equals(e.getMessage())) {\n        // 401 token_expired; client must refresh\n    }\n    throw e;\n}","preventionTips":["Implement a refresh-token flow client-side so tokens are renewed before exp.","Synchronize clocks (NTP) on Nacos and the IdP.","Ensure the IdP always includes an exp claim."],"tags":["oidc","jwt","expiration","clock-skew","claims"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}