spring-projects/spring-security · error · OAuth2AuthorizationException

invalid_token_response

invalid_token_response

Error message

An error occurred parsing the Access Token response: ${ex.getMessage()}

What it means

This OAuth2AuthorizationException with code invalid_token_response is thrown by OAuth2AccessTokenResponseBodyExtractor.parse when the access token response body cannot be parsed as JSON by Nimbus's TokenResponse. A ParseException from the raw JSON string is wrapped with an OAuth2Error describing the parse failure.

Solutions

  1. Log the raw response body to see what was actually returned.
  2. Catch OAuth2AuthorizationException and check getError().getErrorCode() equals invalid_token_response.
  3. Verify the token endpoint returns application/json with token_type/access_token fields per RFC 6749 section 5.1.
  4. Check for proxies/gateways rewriting the response (HTML 502 pages).
  5. If the response was error-shaped, ensure the caller distinguishes OAuth2ErrorResponse handling from token response parsing.

Example fix

// before
TokenResponse response = OAuth2AccessTokenResponseBodyExtractor.parse(body); // throws
// after
try {
    TokenResponse response = OAuth2AccessTokenResponseBodyExtractor.parse(body);
} catch (OAuth2AuthorizationException ex) {
    if ("invalid_token_response".equals(ex.getError().getErrorCode())) {
        logger.warn("Token endpoint returned non-JSON: {}", body);
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (body == null || body.isBlank() || !body.strip().startsWith("{")) {
    throw new IllegalArgumentException("Access token response must be a JSON object");
}

Try / catch

try {
    TokenResponse response = OAuth2AccessTokenResponseBodyExtractor.parse(body);
} catch (OAuth2AuthorizationException ex) {
    if ("invalid_token_response".equals(ex.getError().getErrorCode())) {
        logger.warn("Unparseable token response: {}", body);
    }
}

Prevention

When it happens

Trigger: The access token response body passed to parse is not valid JSON: empty body, HTML error page, or JSON array instead of object (JSONObject constructor or TokenResponse.parse throws ParseException).

Common situations: Authorization server/proxy returns HTML on token endpoint errors (wrong URL, 502 page), response body is empty because content was already consumed, or a mock server returns malformed JSON in tests.

Related errors


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

Appendix: source

Thrown at oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/web/reactive/function/OAuth2AccessTokenResponseBodyExtractor.java:84

		return delegate.extract(inputMessage, context)
			.onErrorMap((ex) -> new OAuth2AuthorizationException(
					invalidTokenResponse("An error occurred parsing the Access Token response: " + ex.getMessage()),
					ex))
			.switchIfEmpty(Mono.error(() -> new OAuth2AuthorizationException(
					invalidTokenResponse("Empty OAuth 2.0 Access Token Response"))))
			.map(OAuth2AccessTokenResponseBodyExtractor::parse)
			.flatMap(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse)
			.map(OAuth2AccessTokenResponseBodyExtractor::oauth2AccessTokenResponse);
	}

	private static TokenResponse parse(Map<String, Object> json) {
		try {
			return TokenResponse.parse(new JSONObject(json));
		}
		catch (ParseException ex) {
			OAuth2Error oauth2Error = invalidTokenResponse(
					"An error occurred parsing the Access Token response: " + ex.getMessage());
			throw new OAuth2AuthorizationException(oauth2Error, ex);
		}
	}

	private static OAuth2Error invalidTokenResponse(String message) {
		return new OAuth2Error(INVALID_TOKEN_RESPONSE_ERROR_CODE, message, null);
	}

	private static Mono<AccessTokenResponse> oauth2AccessTokenResponse(TokenResponse tokenResponse) {
		if (tokenResponse.indicatesSuccess()) {
			return Mono.just(tokenResponse).cast(AccessTokenResponse.class);
		}
		TokenErrorResponse tokenErrorResponse = (TokenErrorResponse) tokenResponse;
		ErrorObject errorObject = tokenErrorResponse.getErrorObject();
		OAuth2Error oauth2Error = getOAuth2Error(errorObject);
		return Mono.error(new OAuth2AuthorizationException(oauth2Error));
	}

	private static OAuth2Error getOAuth2Error(ErrorObject errorObject) {

View on GitHub (pinned to 96852e8860)