spring-projects/spring-security · error · IllegalArgumentException

Missing required parameter: access_token

Error message

Missing required parameter: access_token

What it means

DefaultMapOAuth2AccessTokenResponse.convert requires an access_token parameter in the token endpoint response map per RFC 6749 §5.1. If the map has no access_token, it throws IllegalArgumentException, which callers usually surface wrapped in an OAuth2AuthorizationException or conversion error.

Solutions

  1. Inspect the raw token endpoint response (enable HTTP logging) and fix why access_token is missing
  2. Check that grant_type/client credentials are correct so the server returns a real success response with HTTP 200 and access_token
  3. If the provider returns errors with HTTP 200, add a custom Converter<Map<String,Object>,OAuth2AccessTokenResponse> that detects error fields first and throws OAuth2ErrorException

Example fix

// before
// provider returns {"error":"invalid_grant"} with HTTP 200 -> converter fails
// after
// set a tolerant converter
converter.setAccessTokenResponseConverter(params -> {
  if (params.containsKey("error")) throw new OAuth2ErrorException(new OAuth2Error((String) params.get("error")));
  return defaultConvert(params);
});
Defensive patterns

Strategy: validation

Validate before calling

if (!source.containsKey(OAuth2ParameterNames.ACCESS_TOKEN)) {
    throw new OAuth2AuthorizationException(new OAuth2Error("invalid_token_response", "token response missing access_token", null));
}

Type guard

boolean hasAccessToken(Map<String,Object> body) {
    return body != null && body.get("access_token") instanceof String s && !s.isBlank();
}

Try / catch

catch (IllegalArgumentException e) {
    if (e.getMessage().contains("access_token")) {
        // capture raw response body for diagnosis and fail with clear message
    }
}

Prevention

When it happens

Trigger: The authorization/token endpoint returned a 200 body that lacks access_token — e.g. an error JSON body delivered with HTTP 200, an HTML error page, or a provider returning only refresh_token.

Common situations: Misconfigured token URL hitting a login page; providers deviating from the spec on device-code or extension grants; proxies returning soft-200 error pages; using this converter on a non-token payload.

Related errors


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

Appendix: source

Thrown at oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverter.java:50

/**
 * A {@link Converter} that converts the provided OAuth 2.0 Access Token Response
 * parameters to an {@link OAuth2AccessTokenResponse}.
 *
 * @author Steve Riesenberg
 * @since 5.6
 */
public final class DefaultMapOAuth2AccessTokenResponseConverter
		implements Converter<Map<String, Object>, OAuth2AccessTokenResponse> {

	private static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = new HashSet<>(
			Arrays.asList(OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.EXPIRES_IN,
					OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ParameterNames.SCOPE, OAuth2ParameterNames.TOKEN_TYPE));

	@Override
	public OAuth2AccessTokenResponse convert(Map<String, Object> source) {
		String accessToken = getParameterValue(source, OAuth2ParameterNames.ACCESS_TOKEN);
		if (accessToken == null) {
			throw new IllegalArgumentException("Missing required parameter: " + OAuth2ParameterNames.ACCESS_TOKEN);
		}
		OAuth2AccessToken.TokenType accessTokenType = getAccessTokenType(source);
		long expiresIn = getExpiresIn(source);
		Set<String> scopes = getScopes(source);
		String refreshToken = getParameterValue(source, OAuth2ParameterNames.REFRESH_TOKEN);
		Map<String, Object> additionalParameters = new LinkedHashMap<>();
		for (Map.Entry<String, Object> entry : source.entrySet()) {
			if (!TOKEN_RESPONSE_PARAMETER_NAMES.contains(entry.getKey())) {
				additionalParameters.put(entry.getKey(), entry.getValue());
			}
		}
		// @formatter:off
		return OAuth2AccessTokenResponse.withToken(accessToken)
				.tokenType(accessTokenType)
				.expiresIn(expiresIn)
				.scopes(scopes)
				.refreshToken(refreshToken)
				.additionalParameters(additionalParameters)

View on GitHub (pinned to 96852e8860)