3

I need to get access token (grant_type = client_credentials) in the service layer of my spring boot application to talk to other microservice (service to service interaction). There is no spring http session or auth at this layer, I just have client_id, client_secret and token url. These properties are set in application.properties as:

spring.security.oauth2.client.registration.auth1.client-id=***
spring.security.oauth2.client.registration.auth1.client-secret=***
spring.security.oauth2.client.registration.auth1.authorization-grant-type=client_credentials
spring.security.oauth2.client.provider.auth1.tokenUri=***

This seemed simple with Spring Security OAuth, but can't figure out with Spring security. Referred the documents for spring security 5, but everything seems to be in context of web interface. I understand I could just make http call to get the token with the info I have, but I wanted to utilize the framework...

Scenario: Lets call this spring boot app service A. There are other services which might call A to process updates on http or send kafka message on a topic which A listens to. When A processes the update/message, it needs to send some data to service B which requires access token for authz. This is where I need the access token. So the interaction is essentially service-service and is not specific to user.

coder19
  • 61
  • 1
  • 6
  • `OAuth2RestTemplate` is an API from Spring Security OAuth, a separate plug-in project. The OP is looking to use Spring Security's built-in support. – jzheaux Feb 11 '20 at 16:42

1 Answers1

5

When not in the context of a web interface, you'll want to look at the service layer.

From Spring Security's Javadocs for AuthorizedClientServiceOAuth2AuthorizedClientManager:

An implementation of an {@link OAuth2AuthorizedClientManager} that is capable of operating outside of a {@code HttpServletRequest} context, e.g. in a scheduled/background thread and/or in the service-tier.

Here's a @Bean definition that may help, which I'll explain below:

    @Bean
    OAuth2AuthorizedClientManager authorizedClientManager
            (ClientRegistrationRepository clients) {
        OAuth2AuthorizedClientService service = 
                new InMemoryOAuth2AuthorizedClientService(clients);
        AuthorizedClientServiceOAuth2AuthorizedClientManager manager =
                new AuthorizedClientServiceOAuth2AuthorizedClientManager(clients, service);
        OAuth2AuthorizedClientProvider authorizedClientProvider =
                OAuth2AuthorizedClientProviderBuilder.builder()
                        .clientCredentials()
                        .build();
        manager.setAuthorizedClientProvider(authorizedClientProvider);
        return manager;
    }

An OAuth2AuthorizedClientManager manages authorizing OAuth 2.0 client definitions. These definitions are stored in a ClientRegistrationRepository, and a default instance of ClientRegistrationRepository is created by Spring Boot via the properties you've already got defined.

The manager typically needs two things to function:

  • The first is an OAuth2AuthorizedClientService, which is handy if you are wanting to store tokens in a database - in Spring Security 5.2, the only implementation is an in-memory one; however, it appears that 5.3 will ship with a JDBC implementation.

  • The second is an OAuth2AuthorizedClientProvider, which is what actually performs the token requests, like the client credentials one you want to make.

With this manager created, you can wire it into your web client:

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

The exchange filter function used above is the thing that adds the bearer token to the Authorization header. It calls the manager to ask it for a token, the manager pulls it from the service. If it's expired, the manager asks the provider to refresh it. Now, with a fresh token, the manager hands it back to the filter to get it added into the request.

jzheaux
  • 7,042
  • 3
  • 22
  • 36
  • Thanks! Do you recommend using default WebClient.Builder provided by spring boot for creation of webclient bean? Also I am assuming it is safe to inject this webclient everywhere, as in it is threadsafe. Correction, you probably meant `AuthorizedClientServerOAuth2AuthorizedClientManager`->`AuthorizedClientServiceOAuth2AuthorizedClientManager` – coder19 Feb 12 '20 at 03:15
  • I haven't played very much yet with autowiring `WebClient.Builder`, but I think Brian Clozel offers some interesting perspective here: https://stackoverflow.com/a/54144043/2243324 - The answer I gave follows the pattern in the Spring Security samples, but it's certainly possible those could be improved in that direction. Yes, it should work fine for multiple beans to be dependent on the same web client instance. – jzheaux Feb 12 '20 at 15:16
  • for me, the auth server is behind the proxy so the token calls keep timing out. I have referred Joe's comment here https://github.com/spring-projects/spring-security/issues/8966#issuecomment-684917955 Can we similarly set a webclient for OAuth2AuthorizedClientProvider class? Any help would be very much appreciated! – Denson Sep 23 '21 at 11:49
  • @Denson, I'd recommend you file a separate question with additional detail about your circumstance. For example, it's not clear to me why being behind a proxy would cause a timeout. A separate question will help go into that extra needed detail. Thanks! – jzheaux Sep 23 '21 at 16:55