spring-projects / spring-security

Spring Security
http://spring.io/projects/spring-security
Apache License 2.0
8.71k stars 5.86k forks source link

Unable to customize ReactiveOAuth2AccessTokenResponseClient for refresh token #15657

Open alexist opened 3 weeks ago

alexist commented 3 weeks ago

I'm using spring boot 3.3.1/spring security with oauth2. My Oauth2 / OIDC Provider is behind an Http Proxy. I need to customize the WebClient, in order to configure the HttpProxy.

Following the documentation, i have created 2 beans :

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> authorizationCodeAccessTokenResponseClient() {
    WebClientReactiveAuthorizationCodeTokenResponseClient accessTokenResponseClient =
            new WebClientReactiveAuthorizationCodeTokenResponseClient();
        accessTokenResponseClient.setWebClient(webClient());

    return accessTokenResponseClient;
}

@Bean
public ReactiveOAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenAccessTokenResponseClient() {
    WebClientReactiveRefreshTokenTokenResponseClient accessTokenResponseClient =
            new WebClientReactiveRefreshTokenTokenResponseClient();
        accessTokenResponseClient.setWebClient(webClient());

    return accessTokenResponseClient;
}

private static WebClient webClient() {
    final HttpClient httpClient = HttpClient.create().proxyWithSystemProperties();
    return WebClient.builder()
                    .clientConnector(new ReactorClientHttpConnector(httpClient))
                    .build();
}

When the application receive the authorization code, spring security call the token endpoint and receive the access token. The custom bean WebClientReactiveAuthorizationCodeTokenResponseClient is used.

But when spring security call the endpoint to refresh the token, it doesn't work. The custom WebClientReactiveAuthorizationCodeTokenResponseClient is not used.

I put some breakpoint, the second bean is instantiated but in method RefreshTokenReactiveOAuth2AuthorizedClientProvider.authorize, the accessTokenResponseClient is not my custom bean, but the default one :

@Override
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizationContext context) {
    return Mono.just(refreshTokenGrantRequest)
            .flatMap(this.accessTokenResponseClient::getTokenResponse) // <------ accessTokenResponseClient is the default one
            .onErrorMap(OAuth2AuthorizationException.class,
                    (e) -> new ClientAuthorizationException(e.getError(), clientRegistration.getRegistrationId(), e))
            .map((tokenResponse) -> new OAuth2AuthorizedClient(clientRegistration, context.getPrincipal().getName(),
                    tokenResponse.getAccessToken(), tokenResponse.getRefreshToken()));

The workaround i found is to apply the configuration that should be used prior to 6.3 'Customize WebClient for OAuth2 Client (prior to 6.3') and publish a bean of type ReactiveOAuth2AuthorizedClientManager

sjohnr commented 3 weeks ago

Hi @alexist, thanks for trying the feature and thanks reaching out!

I am not able to reproduce the issue with the information provided. Is it possible that spring-cloud-gateway-server (Spring Cloud Gateway) on the classpath in your application? This is usually added via the org.springframework.cloud:spring-cloud-starter-gateway dependency. If so, please see spring-cloud/spring-cloud-gateway#3493 which may be related.

Otherwise, can you please provide a minimal, reproducible sample to help me find the issue?

alexist commented 3 weeks ago

Hi,

You're right, my project use spring-cloud-starter-gateway.

Thanks for the issue link