spring-cloud/spring-cloud-openfeign

IllegalArgumentException due to null servletRequest for Oauth2AccessTokenInterceptor after Spring Cloud Update

bearoth opened this issue · 3 comments

Describe the bug
I'm currently trying to upgrade spring boot version from 3.1.5 to 3.2.2 and spring cloud version from 2022.0.4 to 2023.0.0 resulting in a org.springframework.cloud.openfeign update from 4.0.4 to 4.1.0 (and a spring-security update form 6.1.5 to 6.2.1 ).

I now get the following exception for feign client requests using a custom oauth configuration.

org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException: No fallback available.
at org.springframework.cloud.client.circuitbreaker.CircuitBreaker.lambda$run$0(CircuitBreaker.java:31)
...
Caused by: java.lang.IllegalArgumentException: servletRequest cannot be null
at org.springframework.util.Assert.notNull(Assert.java:172)
at org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager.authorize(DefaultOAuth2AuthorizedClientManager.java:144)
at org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor.getToken(OAuth2AccessTokenInterceptor.java:131)
at org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor.getToken(OAuth2AccessTokenInterceptor.java:112)
at org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor.apply(OAuth2AccessTokenInterceptor.java:95)
at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:124)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:91)
...

Sample
We need oauth for some FeignClients but also have others that do not need it. We therefore, created the following custom configuration (basically the same as provided in org.springframework.cloud.openfeign.FeignAutoConfiguration):

@Configuration
open class OAuthConfig {
    @Bean
    @ConditionalOnBean(
        OAuth2AuthorizedClientService::class,
        ClientRegistrationRepository::class
    )
    @ConditionalOnMissingBean
    open fun feignOAuth2AuthorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository?,
        oAuth2AuthorizedClientService: OAuth2AuthorizedClientService?
    ): OAuth2AuthorizedClientManager {
        return AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            oAuth2AuthorizedClientService
        )
    }

    @Bean
    @ConditionalOnBean(OAuth2AuthorizedClientManager::class)
    open fun defaultOAuth2AccessTokenInterceptor(
        @Value("\${spring.cloud.openfeign.oauth2.clientRegistrationId:}")
        clientRegistrationId: String?,
        oAuth2AuthorizedClientManager: OAuth2AuthorizedClientManager
    ): OAuth2AccessTokenInterceptor? {
        return OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientManager)
    }
}

And add it as configuration for the FeignClients that need oauth.

@FeignClient(name = "some-client", url = "\${some-url}", configuration = [OAuthConfig::class])
interface SomeClient {
...
}

Any requests for the clients using the OAuthConfig class fail with the aforementioned exception.

Did I miss some configuration that now needs to be added/changed with the update?

Update:
Did some additional investigation. The underlying problem was that the OAuth2AuthorizedClientManager bean started to be instantiated with DefaultOAuth2AuthorizedClientManager instead of AuthorizedClientServiceOAuth2AuthorizedClientManager. I did not find what was the root cause of this though.

I changed the configuration as follows (additionally removed the @Configuration annotation as with the update it started to be used for all FeignClients not just the ones referencing the configuration. This is the desired behavior according to the documentation - just was not aware of this change):

@ConditionalOnClass(OAuth2AuthorizedClientManager::class)
open class OAuthConfig {
    @Bean
    @ConditionalOnMissingBean
    open fun feignOAuth2AuthorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository?,
        oAuth2AuthorizedClientService: OAuth2AuthorizedClientService?
    ): AuthorizedClientServiceOAuth2AuthorizedClientManager? {
        return AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository,
            oAuth2AuthorizedClientService
        )
    }

    @Bean
    @ConditionalOnMissingBean
    open fun defaultOAuth2AccessTokenInterceptor(
        @Value("\${spring.cloud.openfeign.oauth2.clientRegistrationId:}")
        clientRegistrationId: String?,
        oAuth2AuthorizedClientManager: AuthorizedClientServiceOAuth2AuthorizedClientManager?
    ): OAuth2AccessTokenInterceptor? {
        return OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientManager)
    }
}

Hello, @bearoth do I understand correctly that everything works as documented for you? Is there any additional issue?

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.