Search code examples
spring-bootspring-securityoauth-2.0spring-webflux

How to get accesstoken from WebClient like in RestTemplate?


I was going through the Spring Security Oauth 2.x migration to Spring security 5.2 and encountered the following blocker. As I can see OAuth2RestTemplate is not used anymore, instead WebClient is recommended. So i was making changes to my codebase to make webclient work.

In Oauth2 we have an option to get token straight from RestTemplate using oAuth2RestTemplate.getAccessToken(), i couldn't find anything similar in WebClient. This is to call an external microservice. Is there any option to get the accesstoken from webclient? Or is it handled in a different way there?


Solution

  • As usual when working with spring security, a lot of stuff happens automagically with configuration by convention. Meaning: you should get familiar with the oauth-related spring security configuration.

    A good starting point for your studies will be the spring security docu or one of the many good Baeldung articles on this topic (maybe this one helps: https://www.baeldung.com/spring-oauth-login-webflux).

    With the correct configuration in place, the following method will create a WebClient that has the proper oauth token automatically created on demand in the filter method.

    In this case a Bean of type ReactiveOAuth2AuthorizedClientManager takes care of this. That bean is created in listing no. 2.

    Furthermore, you need to configure the oauth server url and the credentials. See Listing 3 for a simple example.

    This example works without you needing to handle the access token.

    
        @Bean
        public WebClient oauthWebClient(
            final WebClient.Builder webClientBuilder,
            @Qualifier("authorizedClientManager") final ReactiveOAuth2AuthorizedClientManager manager) {
            final ExchangeStrategies exchangeStrategies =
                ExchangeStrategies.builder()
                                  .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(EXCHANGE_BYTE_COUNT))
                                  .build();
            final ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(manager);
            oauth.setDefaultClientRegistrationId(authenticationProperties.getClientId());
            // set more properties if needed
            // oauth.set ...
    
            return webClientBuilder
                .exchangeStrategies(exchangeStrategies)
                .baseUrl(apiProperties.getBaseUrl())
                .filter(oauth)
                .build();
        }
    

    Listing 1: create a WebClient bean

        @Bean
        public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
            final ReactiveClientRegistrationRepository clientRegistrationRepository,
            final ReactiveOAuth2AuthorizedClientService authorizedClientService) {
            final ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
                ReactiveOAuth2AuthorizedClientProviderBuilder
                    .builder()
                    .clientCredentials()
                    .build();
            final AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
                new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrationRepository, authorizedClientService);
            authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
            return authorizedClientManager;
        }
    
    

    Listing 2: create an OAuth2 authorized client manager bean

    spring:
      security:
        oauth2:
          resourceserver:
            jwt:
              issuer-uri: https://your-oauth-server.com/auth/realms/your-realm
          client:
            provider:
              your-provider:
                issuer-uri: https://your-oauth-server.com/auth/realms/your-realm
            registration:
              your-provider:
                client-id: your-client-id
                client-secret: ${your_client_secret} # from an environment variable
                scope: openid
                authorization-grant-type: client_credentials
    

    Listing 3: one of many possible ways to configure your spring web client