spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Missing or invalid expire time on OAuth token cause unnecessary reauthorize requests

Open kkurczewski opened this issue 3 years ago • 0 comments

Expected Behavior

I had legacy OAuth2 authorization server which I can't change, it issues Access token without expiration time and without Refresh token.

When I use reactive WebClient with ServerOAuth2AuthorizedClientExchangeFilterFunction filter I would expect just one call for authorization service and reusing token for further requests (until token expiration, 403 response or any other failure occurs).

Current Behavior

Due to missing expire time in token body (which in my case is implicit and defaults to 1 hour) Spring doesn't try to reuse previous token (which I don't like but I can understand) but also always send two Access token requests, one for authorize, and second for reauthorize of "expired" token.

Further explanation, when token doesn't have expire time set it is defaulted by Spring to 1 second and that with default clock skew of 1 minute means that token never can be valid from Spring perspective and re-authorization will always happen (despite resulting in exactly identical expire-in-one-second token).

In summary, I would expect that:

  1. in case no expiration time is present and when Refresh token is not provided then token is not reauthorized at all. This could be also expanded to scenario where expiration token is present but is smaller than specified clock skew.
  2. in case Spring Security don't know how long token is valid then it should try to use it anyway
  3. (optional) some assertion/warning message should inform developer about possible mis-configuration, as last resort note in docs would be helpful.

See related code: ServerOAuth2AuthorizedClientExchangeFilterFunction.java#L340-L351 PasswordReactiveOAuth2AuthorizedClientProvider.java#L95-L108 ClientCredentialsReactiveOAuth2AuthorizedClientProvider.java#L71-L83

Context

Because of this issue unnecessary requests are made which is confusing when looking at logs and it is hard to track down actual root cause.

My current workaround was to write custom body extractor based on OAuth2BodyExtractors.oauth2AccessTokenResponse(), once payload is parsed I use builder OAuth2AccessTokenResponse.withResponse() to add expiration time manually. This works fine but amount of boilerplate I need to provide to make this solution work is too big for scenario which is de facto complaint with actual OAuth standard.

If solution I proposed above is too invasive then I would opt for defaultExpirationTime on Provider config, then I can just put one YML line without calling heavy guns.

kkurczewski avatar Oct 07 '22 07:10 kkurczewski