Describe the bug PasswordOAuth2AuthorizedClientProvider doesn't take refresh token expiry into account. PasswordOAuth2AuthorizedClientProvider only checks that there is a refresh token and then returns null assuming that the RefreshTokenOAuth2AuthorizedClientProvider will get a new token. But if the refresh token has expired, the authorize flow will fail as the RefreshTokenOAuth2AuthorizedClientProvider can't refresh and the PasswordOAuth2AuthorizedClientProvider should have acquired a new authorization token.

To Reproduce create a client provider using OAuth2AuthorizedClientProviderBuilder and add password() and refreshToken(). Use the provider to authorize and thus obtain an authorization token. Wait until the refresh token expires. Try to call authorize again and exception is thrown even though the provider has enough information to acquire a valid token.

Expected behavior Client provider falls back to PasswordOAuth2AuthorizedClientProvider and acquires a new token.

PS. the expiry checks on both PasswordOAuth2AuthorizedClientProvider and RefreshTokenOAuth2AuthorizedClientProvider get the expiry Instant with token.getExpiresAt() which is labeled with Nullable, but they call the .minus method without null check.

Comment From: jgrandja

@jhahkala

PasswordOAuth2AuthorizedClientProvider doesn't take refresh token expiry into account.

See Section 5.1 Successful Response.

It specifies the expires_in parameter for the access token but there is no equivalent for the refresh token. Therefore, there is no way to determine the expiry of the refresh token since there is no standard parameter specified. Furthermore, you will notice that OAuth2AccessTokenResponse.refreshToken.expiresAt defaults to null.

NOTE: Some authorization servers publish their default expiry time for refresh tokens, however, this would need to be customized in the consuming application in order to support refresh token expiry.

the expiry checks on both PasswordOAuth2AuthorizedClientProvider and RefreshTokenOAuth2AuthorizedClientProvider get the expiry Instant with token.getExpiresAt() which is labeled with Nullable, but they call the .minus method without null check.

NPE will not occur in any of these cases since OAuth2AccessTokenResponse.accessToken.expiresAt will default to OAuth2AccessTokenResponse.accessToken.issuedAt plus one second.

I'm going to close this issue and associated PR as the current logic has been implemented to spec.