spring-projects/spring-security · error · OAuth2AuthenticationException

invalid_client

invalid_client

Error message

Client authentication failed: client_id

What it means

PublicClientAuthenticationProvider.authenticate authenticates public OAuth2 clients (which send only client_id, no secret). It looks up the client_id via RegisteredClientRepository; if no RegisteredClient exists for that client_id, it throws an OAuth2AuthenticationException with error code invalid_client, message 'Client authentication failed: client_id'.

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java:84

			OAuth2AuthorizationService authorizationService) {
		Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null");
		Assert.notNull(authorizationService, "authorizationService cannot be null");
		this.registeredClientRepository = registeredClientRepository;
		this.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);
	}

	@Override
	public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
		OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;

		if (!ClientAuthenticationMethod.NONE.equals(clientAuthentication.getClientAuthenticationMethod())) {
			return null;
		}

		String clientId = clientAuthentication.getPrincipal().toString();
		RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);
		if (registeredClient == null) {
			throw invalidClient(OAuth2ParameterNames.CLIENT_ID);
		}

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Retrieved registered client");
		}

		if (!registeredClient.getClientAuthenticationMethods()
			.contains(clientAuthentication.getClientAuthenticationMethod())) {
			throw invalidClient("authentication_method");
		}

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Validated client authentication parameters");
		}

		// Validate the "code_verifier" parameter for the public client
		this.codeVerifierAuthenticator.authenticateRequired(clientAuthentication, registeredClient);

View on GitHub (pinned to 96852e8860)

Solutions

  1. Verify the client_id sent by the client exactly matches a row in your RegisteredClientRepository.
  2. Re-register the client or correct the client-side configuration to the right client_id.
  3. Check the repository backend (JDBC/R2DBC/in-memory) is populated for the active environment/profile.
  4. Enable trace logging on the provider and confirm which client_id value was received.

Example fix

// before: request with unregistered client
// POST /oauth2/token  client_id=my-public-app  (not registered)
// after: register the client first
// RegisteredClient.create().clientId("my-public-app")
//   .clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
//   .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
//   .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
//   .build();
Defensive patterns

Strategy: validation

Validate before calling

RegisteredClient rc = registeredClientRepository.findByClientId(clientId);
if (rc == null) { throw new IllegalStateException("client_id not registered: " + clientId); }

Try / catch

catch (OAuth2AuthenticationException e) { if ("invalid_client".equals(e.getError().getErrorCode())) { verifyClientRegistration(e); } }

Prevention

When it happens

Trigger: A token request from a public client (e.g. PKCE authorization_code, refresh_token) whose clientAuthentication principal (client_id) is not found in the RegisteredClientRepository.

Common situations: Client deleted or renamed in the database while deployed apps still use the old id; typo in client_id; environment mismatch (client registered in prod but request hits staging); JdbcRegisteredClientRepository pointing at wrong schema/data.

Understand the failure class

Related errors


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