Mulgard
Mulgard

Reputation: 10609

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?

Upvotes: 2

Views: 3018

Answers (1)

ch4mp
ch4mp

Reputation: 12825

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:

  • the registration IDs which I changed to make it clearer that the request interceptor instances below reference registration-id and not client-id
  • the disabled default OAuth2 support (which binds to a single client-id)

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!

Upvotes: 5

Related Questions