spring-projects/spring-security · error · OAuth2AuthorizationCodeRequestAuthenticationException
consent_required
consent_required
Error message
OAuth 2.0 Parameter: prompt
What it means
This error is thrown by the authorization code request provider when user consent is required to proceed, but the original authorization request contained prompt=none. Per OIDC, the server must not display any UI for prompt=none, so instead of rendering the consent page it returns an OAuth2 error consent_required attributed to the prompt parameter. It signals that silent authentication could not complete because the client has not yet been granted consent for the requested scopes.
Solutions
- Remove prompt=none from the authorization request (or handle the consent_required error by falling back to an interactive flow with prompt=consent or no prompt) so the user can grant consent.
- Pre-authorize the client for the requested scopes so consent is no longer required (e.g. seed an authorized scope record or store OAuth2Authorization with approved scopes).
- Reduce requested scopes to only those the user has already consented to.
- If using a custom authorizationConsentRequired predicate, adjust it so consent is not required for already-authorized clients/scopes.
Example fix
// before String redirect = "/oauth2/authorize?response_type=code&client_id=my-client&scope=read&prompt=none"; // after (fall back to interactive consent when consent is required) String redirect = "/oauth2/authorize?response_type=code&client_id=my-client&scope=read"; // drop prompt=none, or catch consent_required and retry with prompt=consent
Defensive patterns
Strategy: try-catch
Validate before calling
// before sending the request, ensure prompt=none is only used when consent is already granted
boolean hasPromptNone = request.getPromptValues().contains("none");
boolean consentRequired = requestedScopes.stream().anyMatch(s -> !previouslyAuthorizedScopes.contains(s));
if (hasPromptNone && consentRequired) {
// drop prompt=none or pre-authorize scopes; otherwise consent_required is guaranteed
request = request.withPrompt(PromptValue.CONSENT);
} Try / catch
try {
authenticate(authorizationRequest);
} catch (OAuth2AuthorizationCodeRequestAuthenticationException e) {
if ("consent_required".equals(e.getError().getErrorCode())) {
// retry interactively without prompt=none
redirectToConsentPage(stripPromptNone(originalRequest));
} else {
throw e;
}
} Prevention
- Only use prompt=none for silent flows when the user's consent is known to still be valid
- Pre-seed authorized scopes for clients that rely on silent authentication
- Handle consent_required as a signal to fall back to interactive login, not as a bug
- Keep requested scopes stable; adding scopes invalidates silent flow assumptions
When it happens
Trigger: An OAuth2 authorization code request (OAuth2AuthorizationCodeRequestAuthenticationToken) includes prompt=none among its prompt values, the client is not already authorized for the requested scopes, and the authorizationConsentRequired predicate evaluates to true in OAuth2AuthorizationCodeRequestAuthenticationProvider.authenticate.
Common situations: Single-page or mobile apps doing silent re-authentication/token renewal with prompt=none after the user's consent expired or was revoked; a client adding new scopes that were never consented to; misconfigured RegisteredClient lacking pre-authorized scopes so consent is always required; switching identity providers or clearing authorization records in testing.
Related errors
- insufficient_scope
- invalid_client_metadata
- Invalid Client Registration: + fieldName
- Invalid Client Registration: + fieldName
- invalid_id_token
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/ce7d64c1c4d33148.
Report an issue: GitHub.
Appendix: source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java:266
.authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri())
.clientId(registeredClient.getClientId())
.redirectUri(authorizationCodeRequestAuthentication.getRedirectUri())
.scopes(authorizationCodeRequestAuthentication.getScopes())
.state(authorizationCodeRequestAuthentication.getState())
.additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters())
.build();
authenticationContextBuilder.authorizationRequest(authorizationRequest);
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
.findById(registeredClient.getId(), principal.getName());
if (currentAuthorizationConsent != null) {
authenticationContextBuilder.authorizationConsent(currentAuthorizationConsent);
}
if (this.authorizationConsentRequired.test(authenticationContextBuilder.build())) {
if (promptValues.contains(OidcPrompt.NONE)) {
// Return an error instead of displaying the consent page
throw createException("consent_required", "prompt", authorizationCodeRequestAuthentication,
registeredClient);
}
String state = DEFAULT_STATE_GENERATOR.generateKey();
OAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest)
.attribute(OAuth2ParameterNames.STATE, state)
.build();
if (this.logger.isTraceEnabled()) {
this.logger.trace("Generated authorization consent state");
}
this.authorizationService.save(authorization);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Saved authorization");
}
View on GitHub (pinned to 96852e8860)