spring-security-config:5.3.1.RELEASE
spring-security-oauth2-client:5.3.1.RELEASE
spring-boot-starter-parent:2.2.6.RELEASE
spring-boot-starter-webflux:2.2.6.RELEASE
Is that an issue or am I handling something wrong?
I am not sure if there is a correlation but, the working code example does not retrieve a new token, when Mono.retryWhen(...)
is used.
I experience the same thing. When doing av webClient.get() outside Servlet context i get "servletRequest cannot be null".
If I do the whole call inside a @controller or @RestController it works fine.
@fabian-froehlich @Avec112 The issue here is that the OAuth 2.0 Client Reactive documentation is out-of-date and missing quite a bit of content compared to the Servlet sections.
Take a look at the OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider (Servlet) docs:
The DefaultOAuth2AuthorizedClientManager
is designed to be used within the context of a HttpServletRequest
. When operating outside of a HttpServletRequest
context, use AuthorizedClientServiceOAuth2AuthorizedClientManager
instead.
Since 5.2, it's recommended to use the OAuth2AuthorizedClientManager
constructor.
However, switching the ServerOAuth2AuthorizedClientRepository
to a ReactiveOAuth2AuthorizedClientService
makes the code run.
This makes sense, however, I would recommend using the ReactiveOAuth2AuthorizedClientManager
constructor and pass in AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
.
We have #8174 logged to get the Reactive docs in sync with the Servlet docs.
I'll close this issue as answered. If something is still not clear let me know and we'll address it.
Hi @jgrandja,
thanks for your reply.
If I am understanding you correct, then your recommendet way is what I wrote in my initial post as a running example, right? And it seems that I am outside of a HttpServletRequest
.
If I need to change the code in order to work as expected, could you give an example?
Could you give me an insight, if any possible error here, results in my finding, that Mon.retryWhen(..) does not handle a correct token refresh, when retrys are triggered?
Kind regards,
Fabian Fröhlich
@fabian-froehlich
There are plenty of examples in the reference documentation so please take a look there. Again, the reactive docs are out of date so check out the Servlet docs (the only difference between Servlet and Reactive are the class names).
OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider see AuthorizedClientServiceOAuth2AuthorizedClientManager
(for reactive see AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
)
WebClient integration for Servlet Environments
Hi @jgrandja and sorry for your trouble.
If the only difference is the class name then there might be an issue because the following config still results in an java.lang.IllegalArgumentException: serverWebExchange cannot be null
when following OAuth2AuthorizedClientManager / OAuth2AuthorizedClientProvider and exchanging the classes.
@Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ServerOAuth2AuthorizedClientRepository authorizedClientRepository) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.clientCredentials()
.password()
.build();
DefaultReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
@Bean
WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.filter(oauth)
.build();
@fabian-froehlich
DefaultReactiveOAuth2AuthorizedClientManager
is intended to be used within a request context.
Given that you're seeing serverWebExchange cannot be null
, you must be operating outside of a request context, which in case you should use AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager
instead.
NOTE: Change the ServerOAuth2AuthorizedClientRepository
parameter to ReactiveOAuth2AuthorizedClientService
.
Added WebClientConfiguration class like described above but when I'm trying to run app I see in log:
Consider defining a bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository' in your configuration.
How could it be solved?
Here code of my class:
import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;
@Configuration
@EnableWebFlux
public class WebClientConfiguration {
@Primary
@Bean
public WebClient webClient(ServiceProperties properties,
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager auth2AuthorizedClientManager) {
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(auth2AuthorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("keycloak");
HttpClient httpClient = HttpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(2 * 60 * 1000L, MILLISECONDS));
return WebClient.builder()
.baseUrl(properties.getUrl())
.filter(oauth2Client)
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
@Bean
public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository,
ReactiveOAuth2AuthorizedClientService reactiveOAuth2AuthorizedClientService) {
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, reactiveOAuth2AuthorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
SOLVED.
Need to add ReactiveClientRegistrationRepository bean:
@Bean
ReactiveClientRegistrationRepository clientRegistrations(
@Value("${spring.security.oauth2.client.provider.keycloak.token-uri}") String token_uri,
@Value("${spring.security.oauth2.client.registration.keycloak.client-id}") String client_id,
@Value("${spring.security.oauth2.client.registration.keycloak.client-secret}") String client_secret,
@Value("${spring.security.oauth2.client.registration.keycloak.authorization-grant-type}") String authorizationGrantType
ClientRegistration registration = ClientRegistration
.withRegistrationId("keycloak")
.tokenUri(token_uri)
.clientId(client_id)
.clientSecret(client_secret)
.authorizationGrantType(new AuthorizationGrantType(authorizationGrantType))
.build();
return new InMemoryReactiveClientRegistrationRepository(registration);
and change AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager bean:
@Bean
public AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager(
ReactiveClientRegistrationRepository clientRegistrationRepository) {
InMemoryReactiveOAuth2AuthorizedClientService clientService =
new InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrationRepository);
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider =
ReactiveOAuth2AuthorizedClientProviderBuilder.builder().clientCredentials().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
@andrecampanini, just a note that since the last comment on this thread, we have updated the reactive section of the documentation. See the section on ReactiveOAuth2AuthorizedClientManager. It may be worth reading through the entire chapter in context. If you have anything that looks like a bug, feel free to file a new issue with a minimal sample that reproduces the issue.
in: oauth2
An issue in OAuth2 modules (oauth2-core, oauth2-client, oauth2-resource-server, oauth2-jose)
status: invalid
An issue that we don't feel is valid