prestodb/presto · error · ChallengeFailedException
Missing nonce
Error message
Missing nonce
What it means
NimbusOAuth2Client's OIDC authentication code flow requires a nonce (for replay protection bound into the ID token). getOAuth2Response throws ChallengeFailedException when the caller presents the authorization code without the nonce captured at challenge time.
Source
Thrown at presto-main/src/main/java/com/facebook/presto/server/security/oauth2/NimbusOAuth2Client.java:342
public Request createAuthorizationRequest(String state, URI callbackUri)
{
String nonce = new Nonce().getValue();
return new Request(
new AuthenticationRequest.Builder(CODE, scope, clientId, callbackUri)
.endpointURI(authUrl)
.state(new State(state))
.nonce(new Nonce(hashNonce(nonce)))
.build()
.toURI(),
Optional.of(nonce));
}
@Override
public Response getOAuth2Response(String code, URI callbackUri, Optional<String> nonce)
throws ChallengeFailedException
{
if (!nonce.isPresent()) {
throw new ChallengeFailedException("Missing nonce");
}
OIDCTokenResponse tokenResponse = getTokenResponse(code, callbackUri, OIDCTokenResponse::parse);
OIDCTokens tokens = tokenResponse.getOIDCTokens();
validateTokens(tokens, nonce);
return toResponse(tokens, Optional.empty());
}
@Override
public Response refreshTokens(String refreshToken)
throws ChallengeFailedException
{
OIDCTokenResponse tokenResponse = getTokenResponse(refreshToken, OIDCTokenResponse::parse);
OIDCTokens tokens = tokenResponse.getOIDCTokens();
validateTokens(tokens);
return toResponse(tokens, Optional.of(refreshToken));
}
View on GitHub (pinned to 55bb57d202)
Solutions
- Ensure the challenge step (startChallenge) stores the nonce and the callback passes it back via getOAuth2Response(code, callbackUri, Optional.of(nonce))
- Use sticky sessions / shared state store when running multiple coordinators behind a load balancer
- Clear stale oauth2 state cookies and retry the login flow in a single browser tab
- Check that the HttpServer authentication config consistently uses the OIDC (nonce) path
Example fix
// before client.getOAuth2Response(code, callbackUri, Optional.empty()); // after client.getOAuth2Response(code, callbackUri, Optional.of(savedNonce)); // nonce captured at challenge time
Defensive patterns
Strategy: validation
Validate before calling
if (nonce == null || nonce.isEmpty()) { throw new IllegalStateException("OAuth2 challenge state missing nonce; restart login flow"); } Type guard
boolean hasNonce(java.util.Optional<String> nonce) { return nonce != null && nonce.isPresent() && !nonce.get().isEmpty(); } Try / catch
try { return client.getOAuth2Response(code, callbackUri, nonce); } catch (ChallengeFailedException e) { restartAuthenticationChallenge(); throw e; } Prevention
- Store the nonce at challenge time and pass it through the callback reliably
- Use sticky sessions or a shared state store for multi-node deployments
- Ask users to retry login in a single tab; avoid refreshing old callback URLs
When it happens
Trigger: Completing the OAuth2 redirect flow where the nonce Optional passed to getOAuth2Response is empty — the challenge state was lost, the browser flow was initiated with nonce but the callback lost it, or authentication was initiated with a nonce-less flow but validated against an OIDC client requiring it.
Common situations: Server restart between challenge and callback wiping the nonce from the state store, multiple browser tabs interfering with the oauth2 state cookie, misconfigured load balancer routing callback to a different node that lacks the nonce state, or OAuth2Client configuration toggled from oauth2 to OIDC mid-flow.
Related errors
- Cannot validate tokens
- UNEXPECTED_ACCUMULO_ERROR
- UNEXPECTED_ACCUMULO_ERROR
- Failed to create Credentials from key
- Illegal character ':' found in username
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/14af6def86281a5b.
Report an issue: GitHub.