{"record":{"id":"2ae10db2be1b8310","repo":"spring-projects/spring-security","slug":"invalid-client-2ae10d","errorCode":"invalid_client","errorMessage":"Client authentication failed: client_id","messagePattern":"Client authentication failed: client_id","errorType":"error_code","errorClass":"OAuth2AuthenticationException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java","lineNumber":96,"sourceCode":"\t\tAssert.notNull(registeredClientRepository, \"registeredClientRepository cannot be null\");\n\t\tAssert.notNull(authorizationService, \"authorizationService cannot be null\");\n\t\tthis.registeredClientRepository = registeredClientRepository;\n\t\tthis.codeVerifierAuthenticator = new CodeVerifierAuthenticator(authorizationService);\n\t\tthis.jwtDecoderFactory = new JwtClientAssertionDecoderFactory();\n\t}\n\n\t@Override\n\tpublic @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {\n\t\tOAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication;\n\n\t\tif (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod())) {\n\t\t\treturn null;\n\t\t}\n\n\t\tString clientId = clientAuthentication.getPrincipal().toString();\n\t\tRegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId);\n\t\tif (registeredClient == null) {\n\t\t\tthrow invalidClientException(OAuth2ParameterNames.CLIENT_ID);\n\t\t}\n\n\t\tif (this.logger.isTraceEnabled()) {\n\t\t\tthis.logger.trace(\"Retrieved registered client\");\n\t\t}\n\n\t\t// @formatter:off\n\t\tif (!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.PRIVATE_KEY_JWT) &&\n\t\t\t\t!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {\n\t\t\tthrow invalidClientException(\"authentication_method\");\n\t\t}\n\t\t// @formatter:on\n\n\t\tObject credentials = clientAuthentication.getCredentials();\n\t\tif (credentials == null) {\n\t\t\tthrow invalidClientException(\"credentials\");\n\t\t}\n","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java#L78-L114","documentation":"JwtClientAssertionAuthenticationProvider authenticates clients via a JWT client assertion (private_key_jwt / client_secret_jwt). After validating the assertion, it resolves the client via RegisteredClientRepository.findByClientId using the `iss`/principal of the assertion. If no registered client matches, it throws OAuth2AuthenticationException with error code invalid_client and message 'Client authentication failed: client_id'.","triggerScenarios":"Token request with a signed JWT client_assertion (client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer) whose `iss` (and typically `sub`) claim names a client_id not present in the RegisteredClientRepository — unknown client, wrong environment, or typo in the assertion's iss/sub claims.","commonSituations":"Client not yet registered on the server (missing RegisteredClient with that clientId); assertion issued for a staging client_id used against production; iss/sub mismatch after rotating client configuration; multi-tenant setup where the repository only holds tenants' clients from a different realm; deployment forgot to seed the client into a JDBC-backed RegisteredClientRepository.","solutions":["Register a RegisteredClient with a clientId exactly equal to the JWT assertion's iss/sub claim.","Verify the client is building its assertion with the correct client_id for the target environment.","If using a database-backed RegisteredClientRepository, confirm the client row exists and is loaded (check findByClientId query/tenant filters).","Compare the decoded JWT's iss and sub claims against server-side registered client IDs, and keep the aud claim pointed at the token endpoint /issuer/oauth2/token."],"exampleFix":"// before: assertion built with wrong/stale client id\nJwtClientAssertion.builder().clientId(\"old-client\")...\n\n// after: iss/sub must match a RegisteredClient registered on the server\nRegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())\n    .clientId(\"my-client\")\n    .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)\n    ...build();\nregisteredClientRepository.save(client); // iss/sub of assertion = \"my-client\"","handlingStrategy":"try-catch","validationCode":"// decode assertion payload and verify iss/sub are registered client ids\nString[] parts = clientAssertion.split(\"\\\\.\");\nString iss = json(parse(Base64.getUrlDecoder().decode(parts[1]))).iss;\nif (!registeredClientIds.contains(iss)) {\n    throw new IllegalStateException(\"Client not registered: \" + iss);\n}","typeGuard":null,"tryCatchPattern":"try {\n    TokenResponse resp = tokenClient.withClientAssertion(jwt).getToken();\n} catch (OAuth2AuthenticationException e) {\n    if (\"invalid_client\".equals(e.getError().getErrorCode())) {\n        // verify client registration / assertion iss+sub before retrying\n        throw new ClientRegistrationException(\"Unknown or unregistered client_id\", e);\n    } else { throw e; }\n}","preventionTips":["Keep the RegisteredClientRepository and client configs in sync (same client_id as assertion iss/sub).","Separate credentials per environment; never reuse a client_id across staging/production.","Seed database-backed client registrations during deployment.","Log the assertion's iss/sub (never the signature) when debugging client authentication."],"tags":["oauth2","client-authentication","jwt","invalid-client"],"backgroundTag":"authentication-required","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}