1

I need to consume an API securised by OAuth2 with WebClient. I have configure the OAuth2AuthorizedClientManager to manage the access token and refresh it when it need to be.

However I encounter some issue, java.net.UnknownHostException. There is a proxy between my Application and the OAuth2 token ressource and I do not know how to configure it.

What I have try :

  • Test it in an other environment without proxy and it's work. My OAuth2AuthorizedClientManager configuration is correct.
  • System.setProperty(), not a solution, I have several proxy to manage.

maybe I am misunderstanding some OAuth2 notions

Here some code:

application.properties

spring.security.oauth2.client.registration.client.client-id=clientId
spring.security.oauth2.client.registration.client.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.client.client-secret=clientSecret
spring.security.oauth2.client.provider.client.token-uri=URI/oauth2/token

WebClientConfig

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

        OAuth2AuthorizedClientProvider authorizedClientProvider = 
            OAuth2AuthorizedClientProviderBuilder.builder()
                .clientCredentials()
                .build();

        AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = 
            new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                clientRegistrationRepository, clientService);
        authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

        return authorizedClientManager;
    }

    @Bean
    WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
        ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
                new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
        oauth2Client.setDefaultClientRegistrationId("client");
        return WebClient.builder()
                .baseUrl("URI")
                .clientConnector(getReactorClientHttpConnector(url))
                .apply(oauth2Client.oauth2Configuration())
                .build();
    }

My test

    @Autowired
    WebClient webClient;

    public void test() {
        RequestHeadersSpec<?> request = webClient.get()
                .uri("/heartbeats");
    }

Error

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 "URI/oauth2/token": URI; nested exception is java.net.UnknownHostException: URI

My question is, How to configure a proxy for the OAuth2AuthorizedClientManager ?

Please feel free to ask for clarification.

Any help would be appreciated. Thanks

1 Answers1

0

We had some similar problem in the past and solved it by following configuration.

@Configuration
public class AuthConfiguration {

    @Bean
    public JwtDecoderFactory<ClientRegistration> jwtDecoderFactory() {
        return new CustomOidcIdTokenDecoderFactory(jwksRestTemplate());
    }

    @Bean
    public DefaultAuthorizationCodeTokenResponseClient oAuth2AccessTokenResponseClient() {
        var defaultAuthorizationCodeTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
        defaultAuthorizationCodeTokenResponseClient.setRestOperations(tokenRestTemplate());
        return defaultAuthorizationCodeTokenResponseClient;
    }

    @Bean
    public RestTemplate jwksRestTemplate() {
        return new RestTemplate(requestFactory());
    }

    @Bean
    public RestTemplate tokenRestTemplate() {
        // Copied from constructor of  DefaultAuthorizationCodeTokenResponseClient
        var restTemplate = new RestTemplate(Arrays.asList(
            new FormHttpMessageConverter(), new OAuth2AccessTokenResponseHttpMessageConverter()));
        restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
        restTemplate.setRequestFactory(requestFactory());
        return restTemplate;
    }

    private ClientHttpRequestFactory requestFactory() {
        var requestFactory = new SimpleClientHttpRequestFactory();

        var proxy = new Proxy(Type.HTTP, new InetSocketAddress("my.host.com", 8080));
        requestFactory.setProxy(proxy);

        return requestFactory;
    }
}

Maybe this helps. Also the following class needs to be added because it is package private in Spring ;)

package org.springframework.security.oauth2.client.oidc.authentication;

import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.client.oidc.authentication.DefaultOidcIdTokenValidatorFactory;
import org.springframework.security.oauth2.client.oidc.authentication.OidcIdTokenDecoderFactory;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter;
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtDecoderFactory;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.util.Map;
import java.util.function.Function;

import static org.springframework.security.oauth2.jwt.NimbusJwtDecoder.withJwkSetUri;

/**
 * extension for {@link OidcIdTokenDecoderFactory} to mock the JWKS request
 */
public class CustomOidcIdTokenDecoderFactory implements JwtDecoderFactory<ClientRegistration> {
    private static final String MISSING_SIGNATURE_VERIFIER_ERROR_CODE = "missing_signature_verifier";
    private static final Converter<Map<String, Object>, Map<String, Object>> DEFAULT_CLAIM_TYPE_CONVERTER =
            new ClaimTypeConverter(OidcIdTokenDecoderFactory.createDefaultClaimTypeConverters());
    private Function<ClientRegistration, JwsAlgorithm> jwsAlgorithmResolver = clientRegistration -> SignatureAlgorithm.RS256;
    private Function<ClientRegistration, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = new DefaultOidcIdTokenValidatorFactory();
    private Function<ClientRegistration, Converter<Map<String, Object>, Map<String, Object>>> claimTypeConverterFactory =
            clientRegistration -> DEFAULT_CLAIM_TYPE_CONVERTER;
    private final RestTemplate restTemplate;

    public CustomOidcIdTokenDecoderFactory(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    @Override
    public JwtDecoder createDecoder(ClientRegistration clientRegistration) {
        NimbusJwtDecoder jwtDecoder = buildDecoder(clientRegistration);
        jwtDecoder.setJwtValidator(this.jwtValidatorFactory.apply(clientRegistration));
        Converter<Map<String, Object>, Map<String, Object>> claimTypeConverter =
                this.claimTypeConverterFactory.apply(clientRegistration);
        if (claimTypeConverter != null) {
            jwtDecoder.setClaimSetConverter(claimTypeConverter);
        }
        return jwtDecoder;
    }

    private NimbusJwtDecoder buildDecoder(ClientRegistration clientRegistration) {
        JwsAlgorithm jwsAlgorithm = this.jwsAlgorithmResolver.apply(clientRegistration);
        String jwkSetUri = clientRegistration.getProviderDetails().getJwkSetUri();
        if (!StringUtils.hasText(jwkSetUri)) {
            OAuth2Error oauth2Error = new OAuth2Error(
                    MISSING_SIGNATURE_VERIFIER_ERROR_CODE,
                    "Failed to find a Signature Verifier for Client Registration: '" +
                            clientRegistration.getRegistrationId() +
                            "'. Check to ensure you have configured the JwkSet URI.",
                    null
            );
            throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
        }
        return withJwkSetUri(jwkSetUri).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm).restOperations(restTemplate).build();
    }
}
buderu
  • 415
  • 7
  • 16
  • thanks you for your response ! I have tried your configuration, but I have some issues with the dependencies. Which version are you using for this configuration ? – Guillaume Durandiere Apr 06 '20 at 08:55
  • We use the following maven dependency ` org.springframework.boot spring-boot-starter-oauth2-client2.2.5.RELEASE ` – buderu Apr 07 '20 at 17:54