spring-projects/spring-security · error · OAuth2AuthorizationCodeRequestAuthenticationException

unauthorized_client

unauthorized_client

Error message

OAuth 2.0 Parameter: client_id

What it means

This error is thrown during authorization request validation when the client submits grant_type=authorization_code (or omits it for the code flow) but the RegisteredClient is not registered with the authorization_code grant type. Per OAuth2 spec, authorization servers must reject authorization requests from clients not authorized to use the code flow, reporting unauthorized_client attributed to the client_id parameter.

Solutions

  1. Add AuthorizationGrantType.AUTHORIZATION_CODE to the RegisteredClient's authorization grant types, e.g. RegisteredClient.withId(...).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE).
  2. If the client should not use the code flow, stop sending it to /oauth2/authorize and use its registered grant type (e.g. client_credentials) instead.
  3. Verify the client's stored configuration (YAML, JDBC, etc.) actually includes authorization_code and that the right client_id is being used.

Example fix

// before
RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
    .clientId("my-client")
    .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
    .build();
// after
RegisteredClient client = RegisteredClient.withId(UUID.randomUUID().toString())
    .clientId("my-client")
    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
    .redirectUri("https://example.com/callback")
    .build();
Defensive patterns

Strategy: validation

Validate before calling

// verify before initiating the code flow
if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
    throw new IllegalStateException(
        "Client '" + registeredClient.getClientId() + "' is not registered for authorization_code grant");
}

Try / catch

try {
    authenticate(authorizationRequest);
} catch (OAuth2AuthorizationCodeRequestAuthenticationException e) {
    if ("unauthorized_client".equals(e.getError().getErrorCode())) {
        log.error("Client {} not registered for authorization_code; fix RegisteredClient config", clientId);
    }
    throw e;
}

Prevention

When it happens

Trigger: OAuth2AuthorizationCodeRequestAuthenticationValidator.validateAuthorizationGrantType runs during authentication of an authorization code request and registeredClient.getAuthorizationGrantTypes() does not contain AuthorizationGrantType.AUTHORIZATION_CODE.

Common situations: ClientRegistration/RegisteredClient configured only with client_credentials (or refresh_token) grant but the app still starts the code flow at /oauth2/authorize; a YAML/DB client definition missing authorization_code in its grant types; clients reconfigured after an upgrade (e.g. moving from implicit to code flow) without updating grant types.

Related errors


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

Appendix: source

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

		.andThen(DEFAULT_SCOPE_VALIDATOR);

	@Override
	public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
		this.authenticationValidator.accept(authenticationContext);
	}

	private static void validateAuthorizationGrantType(
			OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
		OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext
			.getAuthentication();
		RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
		if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) {
			if (LOGGER.isDebugEnabled()) {
				LOGGER.debug(LogMessage.format(
						"Invalid request: requested grant_type is not allowed for registered client '%s'",
						registeredClient.getId()));
			}
			throw createException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID,
					authorizationCodeRequestAuthentication, registeredClient);
		}
	}

	private static void validateRedirectUri(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) {
		OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext
			.getAuthentication();
		RegisteredClient registeredClient = authenticationContext.getRegisteredClient();

		String requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri();

		if (StringUtils.hasText(requestedRedirectUri)) {
			// ***** redirect_uri is available in authorization request

			UriComponents requestedRedirect = null;
			try {
				requestedRedirect = UriComponentsBuilder.fromUriString(requestedRedirectUri).build();
			}

View on GitHub (pinned to 96852e8860)