alibaba/nacos · critical · AccessException

Nonce mismatch: expected %s, got %s

Error message

Nonce mismatch: expected %s, got %s

What it means

Thrown by AuthorizationCodeHandler.exchangeCodeForUser when the ID token's nonce claim does not match the nonce embedded in the signed state parameter. The handler compares stateData.nonce with the token's nonce; a mismatch signals a possible token replay or session-fixation attack, so it logs an ERROR and throws AccessException with a formatted mismatch message. Note: the message uses String.format with %s placeholders at runtime.

Source

Thrown at plugin-default-impl/nacos-oidc-auth-plugin/src/main/java/com/alibaba/nacos/plugin/auth/impl/oidc/authenticate/AuthorizationCodeHandler.java:189

            String tokenNonce = (String) claims.getClaim("nonce");
            
            if (tokenNonce == null) {
                String message = "Nonce not present in ID token";
                if (config.isStrictNonceValidation()) {
                    LOGGER.error("{} - Strict validation enabled, rejecting authentication",
                        message);
                    throw new AccessException(message
                        + ". Set 'nacos.plugin.auth.oidc.strict-nonce-validation=false' "
                        + "if your IdP doesn't support nonce.");
                } else {
                    LOGGER.warn("{} - Strict validation disabled, allowing authentication. "
                        + "This reduces protection against replay attacks.", message);
                }
            } else if (!stateData.nonce.equals(tokenNonce)) {
                String message = String.format("Nonce mismatch: expected %s, got %s",
                    stateData.nonce, tokenNonce);
                LOGGER.error("{} - Possible token replay attack detected", message);
                throw new AccessException(message);
            }
            
            // Map claims to user
            OidcUser user = userMapper.mapToUser(claims);
            user.setToken(tokens.getAccessToken().getValue());
            
            LOGGER.info("User authenticated via authorization code: {}", user.getUsername());
            return user;
            
        } catch (AccessException e) {
            throw e;
        } catch (Exception e) {
            LOGGER.error("Failed to exchange code for tokens", e);
            throw new AccessException("Authentication failed: " + e.getMessage());
        }
    }
    
    /**

View on GitHub (pinned to 9b989acdf1)

Solutions

  1. Treat this as a security signal first — investigate whether the state parameter was tampered with in transit.
  2. Ensure no proxy/browser extension rewrites the state or nonce query parameters.
  3. Avoid concurrent OIDC logins in the same browser session that could cross-pair state and tokens.
  4. If the IdP is known to alter nonce, verify its nonce pass-through configuration.
Defensive patterns

Strategy: try-catch

Try / catch

try {
    OidcUser user = handler.exchangeCodeForUser(code, state, redirectUri);
} catch (AccessException e) {
    if (e.getMessage().contains("Nonce mismatch")) {
        // security signal; investigate tampering, do not auto-retry
    }
}

Prevention

When it happens

Trigger: The nonce in the callback's state differs from the nonce in the returned ID token. This can happen if an attacker injects a different state, if the IdP mangles nonce, or if two concurrent login flows cross state and tokens.

Common situations: A real replay/replacement attack; a buggy IdP or proxy that rewrites state or nonce; concurrent browser tabs each starting an OIDC login and the wrong token is paired with the wrong state; client-side state caching bugs.

Related errors


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