spring-projects/spring-security · error · OAuth2AuthenticationException
invalid_scope
invalid_scope
Error message
OAuth 2.0 Parameter: scope
What it means
In OAuth2DeviceAuthorizationConsentAuthenticationProvider.authenticate, the scopes approved by the user in the consent request are compared against the scopes originally requested in the device authorization (stored as the SCOPE attribute on the authorization). If approvedScopes contains any scope not present in requestedScopes, the provider throws invalid_scope with parameter scope — users cannot grant more than what was requested.
Source
Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java:130
if (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) {
throw createException(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE);
}
RegisteredClient registeredClient = this.registeredClientRepository
.findByClientId(deviceAuthorizationConsentAuthentication.getClientId());
if (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) {
throw createException(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Retrieved registered client");
}
Set<String> requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE);
Assert.notNull(requestedScopes, "requestedScopes cannot be null");
Set<String> authorizedScopes = new HashSet<>(deviceAuthorizationConsentAuthentication.getScopes());
if (!requestedScopes.containsAll(authorizedScopes)) {
throw createException(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE);
}
if (this.logger.isTraceEnabled()) {
this.logger.trace("Validated device authorization consent request parameters");
}
OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService
.findById(authorization.getRegisteredClientId(), principal.getName());
Set<String> currentAuthorizedScopes = (currentAuthorizationConsent != null)
? currentAuthorizationConsent.getScopes() : Collections.emptySet();
if (!currentAuthorizedScopes.isEmpty()) {
for (String requestedScope : requestedScopes) {
if (currentAuthorizedScopes.contains(requestedScope)) {
authorizedScopes.add(requestedScope);
}
}
}View on GitHub (pinned to 96852e8860)
Solutions
- Only submit scopes that were requested in the original device authorization request; have the consent UI render checkboxes limited to the requested scopes.
- If the client needs additional scopes, restart the device authorization request including the new scopes.
- Inspect the stored authorization's scope attribute (authorization.getAttribute("scope")) and align the consent scopes with it.
- Remove stale/incorrect authorization records from OAuth2AuthorizationService if scope configuration changed.
Example fix
// before: custom consent page submits unrequested scope
// form: <input name="scope" value="read write admin">
// after: only render requested scopes
// requestedScopes.forEach(s -> out.print("<input name='scope' value='" + s + "'>")); Defensive patterns
Strategy: validation
Validate before calling
Set<String> requested = authorization.getAttribute(OAuth2ParameterNames.SCOPE);
if (requested == null || !requested.containsAll(approvedScopes)) { throw new IllegalStateException("scope exceeds requested scopes"); } Try / catch
catch (OAuth2AuthenticationException e) { if ("invalid_scope".equals(e.getError().getErrorCode())) { renderConsentWithRequestedScopesOnly(); } } Prevention
- Render consent checkboxes only from the stored requested scopes
- Restart device flow when scope requirements change
- Never accept scope values from client-controlled input at consent time
When it happens
Trigger: The consent request carries a scope (e.g. via scope checkbox manipulation or hand-crafted request) that was not part of the scopes stored on the device authorization record; requestedScopes.containsAll(authorizedScopes) fails.
Common situations: A custom consent page allows selecting scopes not included in the original device authorization request; the client changed its requested scopes between initiating device flow and consent; manual/API-driven consent submission injecting extra scopes; stale stored authorization created by an older version of the app with different scopes.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- invalid_scope
- server_error
- OAuth 2.0 Parameter: + parameterName
- insufficient_scope
- Invalidated device code used by registered client '%s'
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/447bf8117182dcb0.
Report an issue: GitHub.