{"record":{"id":"dd33f081f4154cff","repo":"alibaba/nacos","slug":"invalid-or-expired-state-parameter","errorCode":null,"errorMessage":"Invalid or expired state parameter","messagePattern":"Invalid or expired state parameter","errorType":"exception","errorClass":"AccessException","httpStatus":401,"severity":"error","filePath":"plugin-default-impl/nacos-oidc-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/oidc/authenticate/AuthorizationCodeHandler.java","lineNumber":160,"sourceCode":"        }\n    }\n    \n    /**\n     * Exchange authorization code for tokens and authenticate user.\n     *\n     * @param code        authorization code from IdP\n     * @param state       state parameter for CSRF verification\n     * @param redirectUri the redirect URI used in the authorization request\n     * @return authenticated OidcUser\n     * @throws AccessException if authentication fails\n     */\n    public OidcUser exchangeCodeForUser(String code, String state, String redirectUri)\n        throws AccessException {\n        try {\n            // Verify and decode state (self-contained, no cache lookup needed)\n            StateData stateData = verifyAndDecodeState(state);\n            if (stateData == null) {\n                throw new AccessException(\"Invalid or expired state parameter\");\n            }\n            \n            // Exchange code for tokens\n            OIDCTokens tokens = exchangeCodeForTokens(code, redirectUri);\n            \n            // Validate ID token\n            String idTokenString = tokens.getIDTokenString();\n            JWTClaimsSet claims = tokenValidator.validate(idTokenString);\n            \n            // Verify nonce matches (protects against token replay attacks)\n            String tokenNonce = (String) claims.getClaim(\"nonce\");\n            \n            if (tokenNonce == null) {\n                String message = \"Nonce not present in ID token\";\n                if (config.isStrictNonceValidation()) {\n                    LOGGER.error(\"{} - Strict validation enabled, rejecting authentication\",\n                        message);\n                    throw new AccessException(message","sourceCodeStart":142,"sourceCodeEnd":178,"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/authenticate/AuthorizationCodeHandler.java#L142-L178","documentation":"Thrown by AuthorizationCodeHandler.exchangeCodeForUser when verifyAndDecodeState(state) returns null. The state is a self-contained, HMAC-signed, base64-encoded token (nonce.expirationTime.signature). verifyAndDecodeState returns null if the state is malformed (wrong number of parts), signature verification fails, the expiration time is non-numeric, base64 decoding fails, or the state has expired (>10 minutes old). A null result triggers AccessException(\"Invalid or expired state parameter\").","triggerScenarios":"exchangeCodeForUser is called with a state that is tampered, truncated, expired (>10 min), from a different server instance with a different client secret (signature mismatch), or corrupted in transit. The CSRF/state validation fails and returns null.","commonSituations":"The user took more than 10 minutes between initiating login and the IdP callback; the Nacos server's client secret changed between the authorization request and the callback (multi-node with divergent config); the state was URL-mangled by a proxy; an attacker is replaying/altering the state.","solutions":["Ensure the user completes the OIDC login within the 10-minute state validity window.","Confirm all Nacos cluster nodes share the same OIDC client secret (used for HMAC state signing).","Verify proxies/load balancers do not strip or rewrite the state query parameter.","Restart the login flow to get a fresh state if the previous one expired."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    OidcUser user = handler.exchangeCodeForUser(code, state, redirectUri);\n} catch (AccessException e) {\n    if (\"Invalid or expired state parameter\".equals(e.getMessage())) {\n        // prompt user to restart the login flow for a fresh state\n    }\n}","preventionTips":["Complete OIDC login within the 10-minute state validity window.","Ensure all cluster nodes share the same OIDC client secret (used for HMAC state signing).","Verify proxies do not strip or rewrite the state parameter.","Restart the login flow if the callback is delayed."],"tags":["auth","oidc","csrf","state","expired","hmac","access-denied","java"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}