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

  1. Read getError().getErrorCode() from the resulting OAuth2AuthorizationException to see the server's error code (e.g., invalid_token, insufficient_scope, invalid_grant).
  2. If invalid_token: obtain a fresh access token or refresh it before the request.
  3. If insufficient_scope: request the missing scope in the authorization request.
  4. 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

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.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/1eded593cf25fd46. Report an issue: GitHub.