springspring-cloudspring-cloud-feign

How to declare different Oauth2 client registration id's for each feign client?


Im Using Spring Boot 2.7.2 together with Spring Cloud 2021.0. I have multiple OAuth2 providers:

spring:
  security:
    oauth2:
      client:
        provider:
          provider-one:
            authorization-uri: https://provider-one/oauth/authorize
            token-uri: https://provider-one/oauth/token
          provider-two:
            authorization-uri: https://provider-two/oauth/authorize
            token-uri: https://provider-two/oauth/token
        registration:
          client-one:
            provider: provider-one
            client-id: client-one
            authorization-grant-type: client_credentials
          client-two:
            provider: provider-two
            client-id: client-two
            authorization-grant-type: client_credentials

We can define the client registration id for spring cloud openfeign:

spring:
  cloud:
    openfeign:
      oauth2:
        enabled: true
        client-registration-id: client-one

But what do I do if I need FeignClientA to get a token from provider-one and I need FeignClientB to get a token from provider-two? Is there a configurative way to achieve that or do I need to set one interceptor for each feign client myself?


Solution

  • Application properties:

    spring:
      cloud:
        openfeign:
          client:
            config:
              feign-client-one:
                url: https://api-one
              feign-client-two:
                url: https://api-two
          oauth2:
            enabled: false
      security:
        oauth2:
          client:
            provider:
              provider-one:
                authorization-uri: https://provider-one/oauth/authorize
                token-uri: https://provider-one/oauth/token
              provider-two:
                authorization-uri: https://provider-two/oauth/authorize
                token-uri: https://provider-two/oauth/token
            registration:
              registration-one:
                provider: provider-one
                client-id: client-one
                authorization-grant-type: client_credentials
              registration-two:
                provider: provider-two
                client-id: client-two
                authorization-grant-type: client_credentials
    

    Mind:

    And then Feign clients with a request interceptor in conf:

    @FeignClient(name = "feign-client-one", configuration = FeignClientOne.FeignConfiguration.class)
    public interface FeignClientOne {
    
        static class FeignConfiguration {
            @Bean
            OAuth2AccessTokenInterceptor oauth2AccessTokenInterceptorOne(OAuth2AuthorizedClientManager authorizedClientManager) {
                return new OAuth2AccessTokenInterceptor("registration-one", authorizedClientManager);
            }
        }
    }
    
    @FeignClient(name = "feign-client-two", configuration = FeignClientTwo.FeignConfiguration.class)
    public interface FeignClientTwo {
    
        static class FeignConfiguration {
            @Bean
            OAuth2AccessTokenInterceptor oauth2AccessTokenInterceptorTwo(OAuth2AuthorizedClientManager authorizedClientManager) {
                return new OAuth2AccessTokenInterceptor("registration-two", authorizedClientManager);
            }
        }
    }
    

    Edit: Migrating to @HttpExchange Proxies

    It seems that spring-cloud-openfeign is entering "maintenance" mode. The references I could find about that are an answer from @OlgaMaciaszek and this issue on the Github repo.

    I explored a bit around the alternative recommended by Olga and liked it enough to create a starter for REST clients auto-configuration.

    With the same registration properties as above, we can declare RestClient (or WebClient) beans with the following:

    com:
      c4-soft:
        springaddons:
          rest:
            client:
              client-one:
                base-url: http://localhost:7081
                authorization:
                  oauth2:
                    oauth2-registration-id: registration-one
              client-two:
                base-url: http://localhost:7082
                authorization:
                  oauth2:
                    oauth2-registration-id: registration-two
    

    The default names for auto-configured beans are the camelCase transformation of their keys in properties (client-one => clientOne and client-two => clientTwo). This can be changed in properties.

    This auto-configured beans can be injected and used in any Spring @Component. It can also be used in configuration to build @HttpExchange proxies as follows:

    @Configuration
    public class RestConfiguration {
    
      @Bean
      ApiOne apiOne(RestClient clientOne) throws Exception {
        return new RestClientHttpExchangeProxyFactoryBean<>(ApiOne.class, clientOne).getObject();
      }
    
      @Bean
      ApiTwo apiTwo(RestClient clientTwo) throws Exception {
        return new RestClientHttpExchangeProxyFactoryBean<>(ApiTwo.class, clientTwo).getObject();
      }
    }
    

    Where ApiOne and ApiTwo are @HttpExchange interfaces (which are purely declarative, just as @FeignClient). Ideally they are generated from an OpenAPI spec, itself generated from the sources of the API to consume, but these are different stories.

    The code using the RestClient to authorize requests and call the remote APIs is entirely generated!