Proxy Support For Authorization Requests With Spring-Security-OAuth2-Client

19.01.2021

Setting up a proxy for spring-security-oauth2-client authorization requests. Or, how to get rid of the OAuth2AuthorizationException with nested UnknownHostException.

 

 

In our project, one of our new interface partners is using OAuth2 to secure their REST API. We’re using spring-security-oauth2-client to access it. Initially, we had some trouble because the newest version of this library is using WebClient instead of RestTemplate but this isn’t part of this blog post. We developed our feature locally and everything worked as expected. The library works like a charm and manages the access token without us bothering about it. Unlike our development environment, the production environment does not have direct internet access. Since our partner’s interface is on the internet we need to use a proxy so that we can access it. Therefore we added proxy support to our service, deployed it, tested it and surprisingly got the following exception: 

 

Caused by: org.springframework.security.oauth2.core.OAuth2AuthorizationException: [invalid_token_response] An error occurred while attempting to retrieve the OAuth 2.0 Access Token Response: I/O error on POST request for „https://*****/oauth/token“:  *****; nested exception is java.net.UnknownHostException: ***** 

 

The solution

It took us some time to figure out why this is happening. Before I describe the solution, we should recall how OAuth2 is working. First, it sends a request to an authorization server which will return an access token if the permissions are correct and everything is working. This access token will be used in every future request to the resource server as long as it’s valid. 

As the exception – OAuth2AuthorizationException – implies something went wrong during the authorization request. After digging through the source code of spring-security-oauth2-client we found out that the authorization request is using a different client than the resource requests. This means that at that time the proxy was only configured for the resource requests. So we need to configure the proxy for the authorization request separately. Funny enough this request is still using RestTemplate. At least if you’re using the ClientCredentialsOAuth2AuthorizedClientProvider. Enough of text. This is our solution: 

@Bean 
public OAuth2AuthorizedClientManager authorizedClientManager( 
    final ClientRegistrationRepository clientRegistrationRepository, 
    final OAuth2AuthorizedClientService authorizedClientService) { 

    // Create RestTemplate that will be used for the authorization request
    // It's mandatory to add FormHttpMessageConverter and OAuth2AccessTokenResponseHttpMessageConverter
    // See javadoc from DefaultClientCredentialsTokenResponseClient.setRestOperations(RestOperations restOperations)
    // for further information
    RestTemplate restTemplate = new RestTemplate( 
        Arrays.asList(new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter())); 
    restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler()); 

 
    // set up proxy for RestTemplate
    final HttpClientBuilder clientBuilder = HttpClientBuilder.create(); 
    clientBuilder.useSystemProperties(); 
    clientBuilder.setProxy(new HttpHost(proxyConfigDO.getHost(), proxyConfigDO.getPort())); 

    final CredentialsProvider credsProvider = new BasicCredentialsProvider(); 
    credsProvider.setCredentials( 
        new AuthScope(proxyConfigDO.getHost(), proxyConfigDO.getPort()), 
        new UsernamePasswordCredentials(proxyConfigDO.getUser(), proxyConfigDO.getPassword())); 
    clientBuilder.setDefaultCredentialsProvider(credsProvider); 
    clientBuilder.setProxyAuthenticationStrategy(new ProxyAuthenticationStrategy()); 

    final CloseableHttpClient client = clientBuilder.build(); 
    final HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(); 
    factory.setHttpClient(client); 

    restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(factory)); 

    // Create new client and pass our custom resttemplate 
    final var tokenResponseClient = new DefaultClientCredentialsTokenResponseClient(); 
    tokenResponseClient.setRestOperations(restTemplate); 

    // Create ClientCredentialsOAuth2AuthorizedClientProvider and override default setAccessTokenResponseClient
    // with the one we created in this method
    final var authorizedClientProvider = new ClientCredentialsOAuth2AuthorizedClientProvider(); 
    authorizedClientProvider.setAccessTokenResponseClient(tokenResponseClient); 

    final var authorizedClientManager = 
        new AuthorizedClientServiceOAuth2AuthorizedClientManager( 
            clientRegistrationRepository, authorizedClientService); 
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); 

    return authorizedClientManager; 
}

This is just another example of how valuable open sources are. I hope this blogpost helped you solve your problem. Feel free to ask if you have any open questions. 

 

Mehr über Software Development erfahren

Zurück zur Übersicht

3 Kommentare zu “Proxy Support For Authorization Requests With Spring-Security-OAuth2-Client

  1. Hi, sorry to hear that. Could you give us a bit more context?

    I don’t know your project setup, but you might be missing a dependency.

  2. We are having the same problem, but your Solution did not work. When calling
    factory.setHttpClient(client);
    we are getting an error

    Cannot access org.apache.hc.client5.http.classic.HttpClient

  3. Thanks a lot. Had the same issue. Your solution works fine. Hope Spring OAuth2 will move from RestTemplate to WebClient to avoid these issues.

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

*Pflichtfelder

*