spring boot – `servletRequest cannot be null` -> SpringBoot – WebClient – GET request with Keycloak

I’m trying to make GET request between two microservices (with Keycloak authentication).

Let’s say microservice A is asking microservice B for some resources. Microservice B has a GET endpoint and it’s seems to work because I can see correct response when doing request from postman or intelliJ http_client.

In microservice A I’m trying to do a request (I did try to make blocking and non-blocking request):

String response = webClient.mutate()
                .baseUrl(this.serverUri)
                .build().get()
                .uri(uriBuilder -> uriBuilder
                        .path("/users/tokens/{id}")
                        .build(userId))
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction
                        .clientRegistrationId("keycloak"))
                .retrieve()
                .bodyToMono(String.class)
                .doOnError(RuntimeException::new)
                .block();
        webClient.mutate()
                .baseUrl(this.serverUri)
                .build().get()
                .uri(uriBuilder -> uriBuilder
                        .path("/users/tokens/{id}")
                        .build(userId))
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction
                        .clientRegistrationId("keycloak"))
                .retrieve()
                .bodyToMono(String.class)
                .subscribe(resp -> {
                    JSONObject jsonObject = new JSONObject(resp);
                    JSONArray jsonArray = jsonObject.getJSONArray("Tokens");
                    for (int i = 0; i < jsonArray.length(); i++) {
                        log.info("token :: " + jsonArray.get(i).toString());
                    }
                });

This is my WebClient configuration:

@Configuration
public class WebClientConfiguration {

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientRepository authorizedClientRepository) {

        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .refreshToken()
                        .clientCredentials()
                        .build();

        DefaultOAuth2AuthorizedClientManager authorizedClientManager =
                new DefaultOAuth2AuthorizedClientManager(
                        clientRegistrationRepository, authorizedClientRepository);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        return WebClient.builder()
                .apply(oauth2Client.oauth2Configuration())
                .build();
    }
}

Everything I did try ends with this kind of error:

2021-06-29 16:44:07.854 ERROR 390692 --- [oundedElastic-1] reactor.core.publisher.Operators         : Operator called default onErrorDropped

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.IllegalArgumentException: servletRequest cannot be null
Caused by: java.lang.IllegalArgumentException: servletRequest cannot be null
    at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.5.jar:5.3.5]
    Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Error has been observed at the following site(s):
    |_ checkpoint ⇢ Request to GET https://localhost:8080/api/users/tokens/94a2d4f7-b372-4e13-aa16-7b244c099721 [DefaultWebClient]
Stack trace:
        at org.springframework.util.Assert.notNull(Assert.java:201) ~[spring-core-5.3.5.jar:5.3.5]
        at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:144) ~[spring-security-oauth2-client-5.4.5.jar:5.4.5]
        at org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.lambda$authorizeClient$24(ServletOAuth2AuthorizedClientExchangeFilterFunction.java:552) ~[spring-security-oauth2-client-5.4.5.jar:5.4.5]
        at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:85) ~[reactor-core-3.4.4.jar:3.4.4]
        at reactor.core.publisher.FluxSubscribeOnCallable$CallableSubscribeOnSubscription.run(FluxSubscribeOnCallable.java:227) ~[reactor-core-3.4.4.jar:3.4.4]
        at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:68) [reactor-core-3.4.4.jar:3.4.4]
        at reactor.core.scheduler.SchedulerTask.call(SchedulerTask.java:28) [reactor-core-3.4.4.jar:3.4.4]

Am I’m missing something?

[EDIT]

Changing WebClientConfiguration as suggested here (Spring Security 5 Calling OAuth2 Secured API in Application Runner results in IllegalArgumentException) did the trick:

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            ClientRegistrationRepository clientRegistrationRepository,
            OAuth2AuthorizedClientService clientService) {

        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .refreshToken()
                        .clientCredentials()
                        .build();

        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, clientService);

        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        return WebClient.builder()
                .apply(oauth2Client.oauth2Configuration())
                .build();
    }

Can someone explain why?

Leave a Comment