NimbusJwtDecoderJwkSupport uses Nimbus's HTTP Client internally to retrieve the JwkSet resource.

We should re-factor to use RestOperations instead and additionally expose a NimbusJwtDecoderJwkSupport.setRestOperations(restOperations) to allow user's the ability to supply a pre-configured RestOperations if needed.

Related #5601

Comment From: mnovoseltsev

Hi @jgrandja , I wanted to set custom rest operations for NimbusJwtDecoderJwkSupport, but it appears that NimbusJwtDecoderJwkSupport instance is created inside of the private method: OidcAuthorizationCodeAuthenticationProvider#getJwtDecoder. So it is impossible to use NimbusJwtDecoderJwkSupport#setRestOperations method.

For more details - https://github.com/spring-projects/spring-security/issues/6255

Comment From: kumarimanjari

Hi @jgrandja , I have a problem in this area with Spring boot starter oAuth2 client version 3.2.0 which internally uses Spring Security 6.2.0, I have below SecurityConfig file -

I am getting below error - [invalid_id_token] An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.ResourceAccessException: I/O error on GET request for "JWK SET URI": issuer-uri Can someone assist on this issue ?

` required imports

@Configuration @EnableWebSecurity @RequiredArgsConstructor public class SecurityConfig {

@NonNull
private PropertyUtil propertyUtil;

@Bean
public SecurityFilterChain clientSecurityFilterChain(HttpSecurity http) throws Exception {
    http
            .authorizeHttpRequests(requests -> requests.requestMatchers("/", "/api/**").authenticated()
                    .anyRequest().permitAll())
            .oauth2Login(Customizer.withDefaults());
    return http.build();
}

@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
    DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
    accessTokenResponseClient.setRestOperations(restTemplate());

    return accessTokenResponseClient;
}

@Bean
public OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
    DefaultRefreshTokenTokenResponseClient accessTokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
    accessTokenResponseClient.setRestOperations(restTemplate());

    return accessTokenResponseClient;
}

@Bean
public OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsAccessTokenResponseClient() {
    DefaultClientCredentialsTokenResponseClient accessTokenResponseClient = new DefaultClientCredentialsTokenResponseClient();
    accessTokenResponseClient.setRestOperations(restTemplate());

    return accessTokenResponseClient;
}

@Bean
public OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerAccessTokenResponseClient() {
    DefaultJwtBearerTokenResponseClient accessTokenResponseClient = new DefaultJwtBearerTokenResponseClient();
    accessTokenResponseClient.setRestOperations(restTemplate());

    return accessTokenResponseClient;
}

@Bean
public JwtDecoder jwtDecoder() { 
    Proxy proxy = new Proxy(Proxy.Type.HTTP,
            new InetSocketAddress(propertyUtil.getString(PropertyConstants.INTERNET_PROXY_HOST),
                    propertyUtil.getInt(PropertyConstants.INTERNET_PROXY_PORT)));
    SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
    clientHttpRequestFactory.setProxy(proxy);
    ClientHttpRequestFactory bufferedRequestFactory = new BufferingClientHttpRequestFactory(clientHttpRequestFactory);
    RestTemplate restTemplate = new RestTemplate(bufferedRequestFactory);
    return NimbusJwtDecoder
            .withJwkSetUri(jwkSetUri)
            .restOperations(restTemplate).build();
}

@Bean
RestTemplate restTemplate() {

    SimpleClientHttpRequestFactory clientHttpReq = new SimpleClientHttpRequestFactory();
    Proxy proxy = new Proxy(Type.HTTP,
            new InetSocketAddress(propertyUtil.getString(PropertyConstants.INTERNET_PROXY_HOST),
                    propertyUtil.getInt(PropertyConstants.INTERNET_PROXY_PORT)));
    clientHttpReq.setProxy(proxy);

    ClientHttpRequestFactory bufferedRequestFactory = new BufferingClientHttpRequestFactory(clientHttpReq);
    RestTemplate restTemplate = new RestTemplate(Arrays.asList(
            new FormHttpMessageConverter(),
            new OAuth2AccessTokenResponseHttpMessageConverter(),
            new MappingJackson2HttpMessageConverter()));
    restTemplate.setRequestFactory(bufferedRequestFactory);

    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
    if (CollectionUtils.isEmpty(interceptors)) {
        interceptors = new ArrayList<>();
    }
    interceptors.add(new LoggingRestTemplateInterceptor());

    // Default Error Handler from spring security
    restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
    // Logging intercepter
    restTemplate.setInterceptors(interceptors);
    return restTemplate;
}

}`