spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_request

invalid_request

Error message

invalid_request

What it means

Thrown by ClientSecretBasicAuthenticationConverter.convert() when the Authorization header contains the 'Basic' scheme but is not composed of exactly two whitespace-separated parts (scheme + base64 credentials). The library treats a malformed Authorization header as an OAuth2 invalid_request per RFC 6749 section 2.3.1.

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java:64

 * @see OAuth2ClientAuthenticationToken
 * @see OAuth2ClientAuthenticationFilter
 */
public final class ClientSecretBasicAuthenticationConverter implements AuthenticationConverter {

	@Override
	public @Nullable Authentication convert(HttpServletRequest request) {
		String header = request.getHeader(HttpHeaders.AUTHORIZATION);
		if (header == null) {
			return null;
		}

		String[] parts = header.split("\\s");
		if (!parts[0].equalsIgnoreCase("Basic")) {
			return null;
		}

		if (parts.length != 2) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
		}

		byte[] decodedCredentials;
		try {
			decodedCredentials = Base64.getDecoder().decode(parts[1].getBytes(StandardCharsets.UTF_8));
		}
		catch (IllegalArgumentException ex) {
			throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST), ex);
		}

		String credentialsString = new String(decodedCredentials, StandardCharsets.UTF_8);
		String[] credentials = credentialsString.split(":", 2);
		if (credentials.length != 2 || !StringUtils.hasText(credentials[0]) || !StringUtils.hasText(credentials[1])) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST);
		}

		String clientID;
		String clientSecret;

View on GitHub (pinned to 96852e8860)

Solutions

  1. Build the Authorization header as 'Basic ' + Base64(clientId + ':' + clientSecret) with exactly one space between scheme and token.
  2. Verify no proxy, gateway, or client interceptor rewrites or truncates the Authorization header.
  3. If credentials are form-posted instead, use client_secret_post so the Basic converter is not invoked.
  4. Catch OAuth2AuthenticationException on the client side and log the exact outgoing header to confirm its shape.

Example fix

// before
request.setHeader("Authorization", "Basic " + clientId + ":" + secret); // missing Base64 / wrong shape
// after
String token = Base64.getEncoder().encodeToString((clientId + ":" + secret).getBytes(StandardCharsets.UTF_8));
request.setHeader("Authorization", "Basic " + token);
Defensive patterns

Strategy: validation

Validate before calling

boolean validBasicHeader(String header) {
    if (header == null || !header.startsWith("Basic ")) return false;
    return header.split("\\s").length == 2;
}

Try / catch

try { tokenResponse = client.token(request); }
catch (OAuth2AuthenticationException e) {
    if ("invalid_request".equals(e.getError().getErrorCode())) { log.error("Malformed Authorization header", e); }
    throw e;
}

Prevention

When it happens

Trigger: Sending 'Authorization: Basic' with no credentials token, or extra tokens like 'Authorization: Basic abc extra', so parts.length != 2 after header.split("\\s").

Common situations: HTTP clients that strip the base64 credential (proxy or interceptor mangling the header); manual header construction with a missing space or trailing junk; frameworks that fold multiple header values; clients putting 'Basic' in lowercase with concatenated credentials without whitespace.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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