3

How can I configure multiple OAuth2RestTemplates (via OAuth2ProtectedResourceDetails) using Spring Boot so that I can access multiple APIs. They are all configured in same tenant as we see with all configuration being the same except for the scopes.

I believe I did read you cannot have multiple scopes because each JWT token is resource specific but I cannot see examples of having multiple RestTemplates.

Thank you!

security:
  oauth2:
    client:
      client-id: x
      client-secret: y
      user-authorization-uri: z
      access-token-uri: a
      scope: B
      grant-type: client_credentials

    client2:
      client-id: x
      client-secret: y
      user-authorization-uri: z
      access-token-uri: a
      scope: Q
      grant-type: client_credentials
    @Bean(name="ngsWbRestTemplate")
    public OAuth2RestTemplate buildNgsWbRestTemplate(
            OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails
    ){
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails);
        restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
        restTemplate.getAccessToken().getValue();
        return restTemplate;
    }

    @Bean(name="OdpRestTemplate")
    public OAuth2RestTemplate buildOdpRestTemplate(
            OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails,
            @Value("#{propertyService.getValue('ODP_BASE_URI')}") String odpBaseUri
    ){
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(oAuth2ProtectedResourceDetails);
        restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(odpBaseUri));
        restTemplate.setMessageConverters(Collections.singletonList(new MappingJackson2HttpMessageConverter()));
        // test access token retrieval
        restTemplate.getAccessToken().getValue();
        return restTemplate;
    }
Eric Winter
  • 960
  • 1
  • 8
  • 17

1 Answers1

4

I recently made a client that integrates the information of multiple providers who protect their APIs with the OAUth2 protocol. I used this dependency:

<dependency>
  <groupId>org.springframework.security.oauth.boot</groupId>
  <artifactId>spring-security-oauth2-autoconfigure</artifactId>
  <version>2.0.0.RELEASE</version>
</dependency>

In the configuration.yml file you have to set all the properties needed for your client in order to get tokens from Authorization Servers.

example:
 oauth2:
   okta:
     clientId: XXX-XXX-XXX
     clientSecret: XXX-XXX-XXX
     grantType: client_credentials
     accessTokenUri: https://dev-xxxx.okta.com/oauth2/default/v1/token
     scope: custom_service #Created in okta
   keycloak:
     clientId: app-resource-server
     clientSecret: 60bc378c-5c95-4dee-b525-e71993d1596d
     grantType: password  #For password grant_type
     username: user
     password: user
     accessTokenUri: http://localhost:9001/auth/realms/development/protocol/openid-connect/token
     scope: openid profile email

In the main class you need to create a different bean for each resource server that you'd like to send requests with its corresponding access_token in the Authorization header.

@SpringBootApplication
public class DemoApplication {


    /* Inject your client properties into ClientCredentialsResourceDetails object
       if you need to get tokens using client_credentials grant type*/
    @Bean
    @ConfigurationProperties("example.oauth2.okta")
    protected ClientCredentialsResourceDetails oktaOAuth2Details() {
        return new ClientCredentialsResourceDetails();
    }

    /*Inject your client properties into ResourceOwnerPasswordResourceDetails object
      if you need to pass an username and a password*/
    @Bean
    @ConfigurationProperties("example.oauth2.keycloak")
    protected ResourceOwnerPasswordResourceDetails keycloakOAuth2Details() {
        return new ResourceOwnerPasswordResourceDetails();
    }

    //Create the OAuth2RestTemplate bean with the corresponding clientOAuth2Details
    @Bean("oktaOAuth2RestTemplate")
    protected OAuth2RestTemplate oktaOAuth2RestTemplate() {
        return new OAuth2RestTemplate(oktaOAuth2Details());
    }

    @Bean("keycloakOAuth2RestTemplate")
    protected OAuth2RestTemplate keycloakOAuth2RestTemplate() {
        return new OAuth2RestTemplate(keycloakOAuth2Details());
    }

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Then you can autowire the OAuth2RestTemplate choosing the desired implementation with the @Qualifier annotation as shown below

@Component
public class AsyncService {

    //@Value("#{ @environment['example.baseUrl'] }")
    private static final String API_URL1 = "http://localhost:8081";
    private static final String API_URL2 = "http://localhost:8082";

    private final Logger log = LoggerFactory.getLogger(AsyncService.class);

    @Autowired
    @Qualifier("oktaOAuth2RestTemplate")
    OAuth2RestTemplate oktaOAuth2RestTemplate;

    @Autowired
    @Qualifier("keycloakOAuth2RestTemplate")
    OAuth2RestTemplate keycloakOAuth2RestTemplate;

    public void oktaRequest() {
        log.info("Okta access_token: {}", oktaOAuth2RestTemplate.getAccessToken());
        log.info(oktaOAuth2RestTemplate.getForObject(API_URL1 + "/message", String.class));
    }

    public void keylcloakRequest() {
        log.info("Keycloak access_token: {}", keycloakOAuth2RestTemplate.getAccessToken());
        log.info(keycloakOAuth2RestTemplate.getForObject(API_URL2 + "/message", String.class));
    }

    //@Async
    public void requestMessage() {
        oktaRequest();
        keycloakRequest();
    }
}

Spring will refresh the tokens automatically when they expire and that's so cool.

Ivo Mori
  • 2,177
  • 5
  • 24
  • 35
rortegat
  • 61
  • 7