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
- Verify the client_id sent by the client exactly matches a row in your RegisteredClientRepository.
- Re-register the client or correct the client-side configuration to the right client_id.
- Check the repository backend (JDBC/R2DBC/in-memory) is populated for the active environment/profile.
- 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
- Keep client registrations in sync across environments
- Validate client_id in client config at startup
- Check repository data after migrations
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/c4c945514c634e39.
Report an issue: GitHub.