TokenRelayGatewayFilter always fetches access token via refresh token
Describe the bug Note: this is a bug report which originates from https://github.com/spring-projects/spring-security/issues/10082. The original Bug in that issue was already fixed.
When using TokenRelayGatewayFilterFactory for passing access tokens to downstream services, that filter always fetches the access token via the the refresh token which is stored within the current users session. This is unnecessary since a session still might have a valid access token stored, which is not yet expired. This can be easily reproduced by creating a sample application and set the breakpoint in WebClientReactiveRefreshTokenTokenResponseClient#populateTokenRequestBody which is always called.
I implemented a Workaround which first checks the access token before fetching a new access token via the refresh token:
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient
import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientManager
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken
import org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository
import org.springframework.security.oauth2.core.OAuth2AccessToken
import org.springframework.stereotype.Service
import org.springframework.web.server.ServerWebExchange
import reactor.core.publisher.Mono
import java.security.Principal
import java.time.Instant
@Service
class Oauth2AccessTokenProvider(
@Autowired private val serverOAuth2AuthorizedClientRepository: ServerOAuth2AuthorizedClientRepository,
@Autowired private val clientManager: ReactiveOAuth2AuthorizedClientManager
) {
fun provideAccessToken(exchange: ServerWebExchange): Mono<OAuth2AccessToken> {
return exchange.getPrincipal<Principal>()
.filter { it is OAuth2AuthenticationToken }
.cast(OAuth2AuthenticationToken::class.java)
.flatMap { token ->
serverOAuth2AuthorizedClientRepository.loadAuthorizedClient<OAuth2AuthorizedClient>(
token.authorizedClientRegistrationId,
token,
exchange
)
.map { client -> client.accessToken }
.filter { accessToken -> accessToken.expiresAt?.isAfter(Instant.now()) ?: false }
.switchIfEmpty(fallbackFetchToken(exchange, token))
}
}
private fun fallbackFetchToken(
exchange: ServerWebExchange,
oauth2Authentication: OAuth2AuthenticationToken
): Mono<OAuth2AccessToken> {
val clientRegistrationId = oauth2Authentication.authorizedClientRegistrationId
val request = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId)
.attribute(ServerWebExchange::class.java.name, exchange)
.principal(oauth2Authentication).build()
return clientManager.authorize(request)
.map { it.accessToken }
}
}
Sample If possible, please provide a test case or sample application that reproduces the problem. This makes it much easier for us to diagnose the problem and to verify that we have fixed it.
Are you willing to submit a PR?
Hi @spencergibb , I create fix PR https://github.com/spring-cloud/spring-cloud-gateway/pull/3202! Can you simply check my PR? thanks! 🙇
Hi @injae-kim. Thanks for the PR and taking a deep look at the token relay filter!
Before proceeding, can we review your configuration and discuss why the issue you're facing may be happening? Spring Security's ReactiveOAuth2AuthorizedClientManager will automatically handle re-using access tokens if they are still active. It seems possible you have an incorrect configuration. Can you share a minimal reproducible sample that demonstrates the behavior you're seeing?
Spring Security's ReactiveOAuth2AuthorizedClientManager will automatically handle re-using access tokens if they are still active.
oh I just think that this issue is still valid and need to fix. 😅
sorry for bothering you, I'll close my PR~ then we can close this issue too?
oh I just think that this issue is still valid and need to fix. 😅
@injae-kim, thanks for clarifying. I'm not convinced this is a valid issue, but I'd like to understand it more. Are you saying you did not encounter this issue yourself?
sorry for bothering you, I'll close my PR~ then we can close this issue too?
No problem @injae-kim. I think we need to check with the reporter to see if we can get more information. @viZu, perhaps you can provide some more information? See above comment.
Are you saying you did not encounter this issue yourself?
yep! by https://github.com/spring-cloud/spring-cloud-gateway/issues/2316#issuecomment-937343720, I think this issue is verified as bug so I can create PR :)
Let's wait more information~!
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.