Expected Behavior
I'd expect to have a method similar to
http.oauth2ResourceServer(oauth2 -> oauth2.webClient(myCustomWebClient()))
Current Behavior
Web client is created in the AbstractWebClientReactiveOAuth2AccessTokenResponseClient:
private WebClient webClient = WebClient.builder().build();
Context
How has this issue affected you?
We are suffering from exceptions thrown when requesting resource server endpoints:
org.springframework.web.reactive.function.client.WebClientRequestException: recvAddress(..) failed: Connection reset by peer; nested exception is io.netty.channel.unix.Errors$NativeIoException: recvAddress(..) failed: Connection reset by peer
at org.springframework.web.reactive.function.client.ExchangeFunctions$DefaultExchangeFunction.lambda$wrapException$9(ExchangeFunctions.java:141)
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Request to POST https://xxxxx/protocol/openid-connect/token [DefaultWebClient]
Default web client settings are different than our AWS setup (max idle connection time in pool, etc ) so it's crucial for us to configure the web client used by the oauth2 implementation. (Please see that this is causing trouble for other netty client users https://github.com/reactor/reactor-netty/issues/1774)
What are you trying to accomplish?
Get stable oauth2 authentication in our environment
What other alternatives have you considered?
Provide own oauth2.authenticationManagerResolver, but it will be an overkill just for setting up web client max idle time
Are you aware of any workarounds? No
Comment From: maciejwitwicki
We have found a way to set the client, but it looks like way too much obfuscated:
So the underlying web-client configuration is done with the "main" oauth web-client configuration (we needed to provide a client while building a client - this is way different from what I proposed in the original post).
(works for spring-boot version 2.7.5)
WebClient that will be used by our app (to do http calls to some business resources):
WebClient.builder()
.clientConnector(...)
.baseUrl(...)
.filter(oauth)
.build()
The oauth is as follows:
var clientService = new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations);
var authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, clientService);
authorizedClientManager.setAuthorizedClientProvider(clientProvider(clientHttpConnector));
var oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
Now the clientProvider(...) method creates provider with:
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials(builder -> builder.accessTokenResponseClient(tokenClient(clientHttpConnector)))
.build();
And finally, the tokenClient(...) creates our own managed client as follows:
var tokenClient = new WebClientReactiveClientCredentialsTokenResponseClient();
var webClient = WebClient.builder()
.clientConnector(clientHttpConnector)
.build();
tokenClient.setWebClient(webClient);
So this is the client you want to configure with your own pool settings.
I would definitely prefer to have it exposed by the sdk somewhere higher....
Comment From: ionutgyn
Now there is a why to do this
@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> reactiveOAuth2AccessTokenResponseClient() {
WebClient webClient = WebClient.builder().clientConnector(new ReactorClientHttpConnector(HttpClient.create(connectionProvider()))).build();
WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient = new WebClientReactiveAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setWebClient(webClient);
return accessTokenResponseClient;
}
private ConnectionProvider connectionProvider() {
return ConnectionProvider.builder("http")
.maxConnections(connectionPoolConfig.getMaxConnections())
.maxLifeTime(connectionPoolConfig.getMaxLifeTime())
.build();
}
Comment From: jzheaux
@maciejwitwicki, thanks for reaching out.
I'm not sure I understand the concern. In your description, you talk about resource server configuration, and then in a subsequent post you talk about client configuration.
If what you are primarily concerned about is client configuration, please see https://github.com/spring-projects/spring-security/issues/8882 for some background. To participate in the discussion further, follow to the end of that issue to find the current open related issues.
As for Resource Server specifically, you can configure a web client in the following way:
@Bean
ReactiveJwtDecoder jwtDecoder() {
return NimbusReactiveJwtDecoder.withIssuerLocation("http://issuer.example.org").webClient(web).build();
}
Either way, I'm going to close this ticket as answered, though please feel free to discuss further if it feels like the above doesn't fully address your concern.