Skip to content

Additional client_id field added in POST body for private_key_jwt authentication method for client credential grant type #11298

Closed
@mit2222

Description

@mit2222

Describe the bug
I specifically want to use WebClientReactiveClientCredentialsTokenResponseClient because it provides WebClient to integrate with Okta api with client credentials private_key_jwt. Okta's /v1/token url needs client_assertion_type of urn:ietf:params:oauth:client-assertion-type:jwt-bearer, grant type as client_credentials and authentication method as PRIVATE_KEY_JWT.

To Reproduce
I would like to retrieve access token via client_credentials private_key_jwt flow through Spring Boot WebClient in-memory solution.
Upon debugging, client_id gets added as a result of which the body consists client_assertion, client_assertion_type, scope,grant_type and client_id due to AbstractWebClientReactiveOAuth2AccessTokenResponseClient class populateTokenRequestBody() private method.

I tried to manually integrate with Okta v1/token url through postman with client_assertion value retrieved from jwksResolver and I do get a valid Bearer token.

Expected behavior
I get below error

{
    "errors": [
        {
            "status": "500",
            "title": "INTERNAL_SERVER_ERROR",
            "detail": "[invalid_request] Cannot supply multiple client credentials. Use one of the following: credentials in the Authorization header, credentials in the post body, or a client_assertion in the post body."
        }
    ]
}

I have the below bean configurations.

 @Bean
    ReactiveClientRegistrationRepository clientRegistrations() {

        ClientRegistration registration = ClientRegistration
                .withRegistrationId("OktaExample")
                .tokenUri("https://{oktaDomain}/oauth2/v1/token")
                .clientId(clientId)
                .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT)
                .scope(scope)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .build();
        return new InMemoryReactiveClientRegistrationRepository(registration);
    }

   @Bean
    public ReactiveOAuth2AuthorizedClientService authorizedClientService(
            ReactiveClientRegistrationRepository clientRegistrations) {
        return new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations);
    }

   @Bean
    public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
            ReactiveClientRegistrationRepository clientRegistrations,
            ReactiveOAuth2AuthorizedClientService authorizedClientService) {
        return configureHttpProxy(
                new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
                        clientRegistrations,
                        authorizedClientService
                ));
    }

private AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager configureHttpProxy(AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
        
        Function<ClientRegistration, JWK> jwkResolver = client -> {
                return new RSAKey.Builder(publicKey)
                        .privateKey(privateKey)
                        .build();
        };

        WebClientReactiveClientCredentialsTokenResponseClient tokenResponseClient = new WebClientReactiveClientCredentialsTokenResponseClient();
        tokenResponseClient.addParametersConverter(new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));

        tokenResponseClient.setWebClient(
                WebClient.builder().build()
        );

        ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider= ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
                .clientCredentials(clientCredentialsGrantBuilder ->
                        clientCredentialsGrantBuilder.accessTokenResponseClient(tokenResponseClient)) 
                .build();


        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

   @Bean
    WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
                

       ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("OktaExample");

        return WebClient.builder()
                .filters(exchangeFilterFunctions -> {
                    exchangeFilterFunctions.add(oauth2Client);
                })
                .build();
    }

Metadata

Metadata

Assignees

Labels

in: oauth2An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)status: duplicateA duplicate of another issuetype: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions