spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_request

invalid_request

Error message

OAuth 2.0 Client Registration Error: ${ex.getMessage()}

What it means

OAuth2ClientRegistrationAuthenticationConverter wraps any exception raised while parsing a dynamic client registration request (RFC 7591) into an OAuth2AuthenticationException with code invalid_request and description "OAuth 2.0 Client Registration Error: <original message>". The catch-all in convert() means any failure deserializing/validating the registration JSON body surfaces under this single message, preserving the cause in the exception.

Solutions

  1. Look at the cause (ex.getMessage() appears in the description) to see the actual parse/validation failure.
  2. Validate the registration JSON against RFC 7591 metadata: required fields such as client_name, redirect_uris, grant_types, token_endpoint_auth_method.
  3. Send the request with Content-Type: application/json and a syntactically valid JSON body.
  4. Catch OAuth2AuthenticationException on the client side and surface the error_description from the registration endpoint's 400 response.

Example fix

// before: malformed registration payload
{"client_name":"my-app","redirect_uris":"https://app/callback"}
// after
{"client_name":"my-app",
 "redirect_uris":["https://app.example.com/callback"],
 "grant_types":["authorization_code"],
 "token_endpoint_auth_method":"client_secret_basic"}
Defensive patterns

Strategy: try-catch

Validate before calling

function validateRegistrationPayload(payload) {
  const errors = [];
  if (typeof payload !== 'object' || payload === null) errors.push('body must be a JSON object');
  if (!Array.isArray(payload.redirect_uris) || payload.redirect_uris.length === 0) errors.push('redirect_uris must be a non-empty array');
  if (!Array.isArray(payload.grant_types) || payload.grant_types.length === 0) errors.push('grant_types must be a non-empty array');
  if (!payload.token_endpoint_auth_method) errors.push('token_endpoint_auth_method is required');
  return errors;
}

Type guard

function isRegistrationPayload(p) {
  return p != null && typeof p === 'object'
    && typeof p.client_name === 'string'
    && Array.isArray(p.redirect_uris)
    && Array.isArray(p.grant_types);
}

Try / catch

try {
  response = registerClient(registrationJson);
} catch (OAuth2AuthenticationException e) {
  OAuth2Error err = e.getError();
  logger.error("Client registration failed ({}): {} cause={}",
      err.getErrorCode(), err.getDescription(),
      e.getCause() != null ? e.getCause().getMessage() : "n/a");
  // fix payload per the cause message and retry
}

Prevention

When it happens

Trigger: A POST to the client registration endpoint with a body that fails to parse as a ClientRegistration payload (invalid JSON, wrong field types, missing required fields like redirect_uris or token_endpoint_auth_method, or any other exception thrown by the underlying request parser).

Common situations: Sending registration metadata with a wrong-typed field (e.g. numeric grant type); omitting required RFC 7591 fields; sending a content-type other than application/json; malformed JSON from a template or script; provisioning clients with an automation script that has drifted from the expected schema.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

 */
public final class OAuth2ClientRegistrationAuthenticationConverter implements AuthenticationConverter {

	private final HttpMessageConverter<OAuth2ClientRegistration> clientRegistrationHttpMessageConverter = new OAuth2ClientRegistrationHttpMessageConverter();

	@Override
	public Authentication convert(HttpServletRequest request) {
		Authentication principal = SecurityContextHolder.getContext().getAuthentication();

		OAuth2ClientRegistration clientRegistration;
		try {
			clientRegistration = this.clientRegistrationHttpMessageConverter.read(OAuth2ClientRegistration.class,
					new ServletServerHttpRequest(request));
		}
		catch (Exception ex) {
			OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST,
					"OAuth 2.0 Client Registration Error: " + ex.getMessage(),
					"https://datatracker.ietf.org/doc/html/rfc7591#section-3.2.2");
			throw new OAuth2AuthenticationException(error, ex);
		}

		return new OAuth2ClientRegistrationAuthenticationToken(principal, clientRegistration);
	}

}

View on GitHub (pinned to 96852e8860)