spring-projects/spring-security · error · OAuth2AuthorizationException
OAuth2Error read from WWW-Authenticate header or error respo
Error message
OAuth2Error read from WWW-Authenticate header or error response body (dynamic)
What it means
OAuth2ErrorResponseErrorHandler is installed as the RestClient response error handler for OAuth2 calls. When the resource/token endpoint replies with an error status, it parses a Bearer-token error from the WWW-Authenticate header (RFC 6750) or, failing that, from the JSON error body, and throws OAuth2AuthorizationException carrying that OAuth2Error. The message is dynamic — it reflects the upstream server's error code/description.
Source
Thrown at oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java:69
private final ResponseErrorHandler defaultErrorHandler = new DefaultResponseErrorHandler();
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return this.defaultErrorHandler.hasError(response);
}
@Override
public void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
if (HttpStatus.BAD_REQUEST.value() != response.getStatusCode().value()) {
this.defaultErrorHandler.handleError(url, method, response);
}
// A Bearer Token Error may be in the WWW-Authenticate response header
// See https://tools.ietf.org/html/rfc6750#section-3
OAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());
if (oauth2Error == null) {
oauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);
}
throw new OAuth2AuthorizationException(oauth2Error);
}
private @Nullable OAuth2Error readErrorFromWwwAuthenticate(HttpHeaders headers) {
String wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE);
if (!StringUtils.hasText(wwwAuthenticateHeader)) {
return null;
}
BearerTokenError bearerTokenError = getBearerToken(wwwAuthenticateHeader);
if (bearerTokenError == null) {
return new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, null, null);
}
String errorCode = (bearerTokenError.getCode() != null) ? bearerTokenError.getCode()
: OAuth2ErrorCodes.SERVER_ERROR;
String errorDescription = bearerTokenError.getDescription();
String errorUri = (bearerTokenError.getURI() != null) ? bearerTokenError.getURI().toString() : null;
return new OAuth2Error(errorCode, errorDescription, errorUri);
}
View on GitHub (pinned to 96852e8860)
Solutions
- Read getError().getErrorCode() from the resulting OAuth2AuthorizationException to see the server's error code (e.g., invalid_token, insufficient_scope, invalid_grant).
- If invalid_token: obtain a fresh access token or refresh it before the request.
- If insufficient_scope: request the missing scope in the authorization request.
- If invalid_grant: fix the credentials/grant being sent to the token endpoint.
Example fix
// before: reuse a cached token forever
String token = cachedToken;
// after: refresh on invalid_token
if (isOAuth2Error(ex, "invalid_token")) { token = refreshAccessToken(); retry(request, token); } Defensive patterns
Strategy: try-catch
Type guard
static boolean isBearerError(OAuth2AuthorizationException ex, String code) {
return ex.getError() != null && code.equals(ex.getError().getErrorCode());
} Try / catch
try {
ResponseEntity<String> r = rest.exchange(url, GET, new HttpEntity<>(headers(authToken)), String.class);
} catch (HttpStatusCodeException ex) {
// handler will translate this into OAuth2AuthorizationException with the bearer error
if (ex.getResponseHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE) != null) {
refreshTokenAndRetry();
}
} Prevention
- Refresh access tokens proactively before expiry instead of waiting for invalid_token.
- Check WWW-Authenticate on any 401 to learn the exact bearer error code.
- Request all needed scopes up front to avoid insufficient_scope.
- Never cache access tokens longer than their expires_in.
When it happens
Trigger: Thrown in handleError() whenever the protected endpoint returns an HTTP error status: e.g., WWW-Authenticate: Bearer error="invalid_token", or an error body like {"error":"invalid_grant"}.
Common situations: Expired or revoked access token used against a resource server (invalid_token/insufficient_scope in WWW-Authenticate), token endpoint rejecting the grant (invalid_grant), or calling userinfo with a malformed token.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/1eded593cf25fd46.
Report an issue: GitHub.