Describe the bug
Apparently, neither the ID token nor the userinfo are updated during the refresh token flow in Spring clients with oauth2Login. This has at least two consequences:
the principal (OidcUser or OAuth2User) might contain outdated data
in case of an RP-Initiated Logout, the OidcClientInitiated(Server)LogoutSuccessHandler might build a redirection URI to the authorization server end_session_endpoint with an expired or outdated ID token, in which case the second part of the logout fails and the user session on the OpenID Provider might not be ended.
Additional context about ID tokens
ID tokens are JWTs and, as with all JWTs, they expire.
During RP-Initiated Logout, OpenID Providers SHOULD (not MUST) accept ID tokens even when the exp time has passed. This means that some OPs might not accept expired tokens and that clients would better do their best to send valid tokens.
Also, aside from expiration considerations, most OPs refreshing ID tokens accept only the last token they issued for a client. So when a new ID token is returned as part of a refresh token flow, the client should use this last issued ID token to build the RP-Initiated Logout location URI.
Last, I know no constraint in the specs about the different tokens relative lifespans. As a consequence, it seems possible that an ID token expires before the access one. And as far as I understood the source, the RefreshToken(Reactive)OAuth2AuthorizedClientProviderrefreshes tokens only if the access token expired which leaves room for expired ID token in the security context.
To Reproduce
Using an authorization server refreshing ID token as part of the refresh token flow (Keyckloak is one, and, according to the Stackoverflow question linked below, Spring Authorization Server seems to be another):
login to an oauth2Client configured with oauth2Login and RP-Initated Logout
wait until the access token is expired
have refresh token flow executed. Using the (Reactive)OAuth2AuthorizedClientManager to retrieve the access token - like Spring Cloud Gateway TokenRelay= filter does - is enough.
initiate RP-Initiated Logout (send a POST request to the /logout endpoint)
Expected behavior
tokens should be refreshed if either the access or the ID token has expired (or will before the clockSkew)
if the security context contains an ID token (the principal is an OidcUser) and if the refresh token response contains an ID token, then the ID token in the security context should be updated
redirection to the authorization server end_session_endpoint should be built with the last issued ID token
The OidcClientInitiated(Server)LogoutSuccessHandler currently uses the (Reactive)ClientRegistrationRepository which doesn't trigger the refresh token flow in case of expired tokens. Shouldn't it use the (Reactive)OAuth2AuthorizedClientManager instead?
Some StackOverflow questions related to this issue:
Describe the bug Apparently, neither the ID token nor the
userinfo
are updated during the refresh token flow in Spring clients withoauth2Login
. This has at least two consequences:OidcUser
orOAuth2User
) might contain outdated dataOidcClientInitiated(Server)LogoutSuccessHandler
might build a redirection URI to the authorization serverend_session_endpoint
with an expired or outdated ID token, in which case the second part of the logout fails and the user session on the OpenID Provider might not be ended.Additional context about ID tokens ID tokens are JWTs and, as with all JWTs, they expire.
ID tokens can be refreshed.
During RP-Initiated Logout, OpenID Providers SHOULD (not MUST) accept ID tokens even when the exp time has passed. This means that some OPs might not accept expired tokens and that clients would better do their best to send valid tokens.
Also, aside from expiration considerations, most OPs refreshing ID tokens accept only the last token they issued for a client. So when a new ID token is returned as part of a refresh token flow, the client should use this last issued ID token to build the RP-Initiated Logout location URI.
Last, I know no constraint in the specs about the different tokens relative lifespans. As a consequence, it seems possible that an ID token expires before the access one. And as far as I understood the source, the
RefreshToken(Reactive)OAuth2AuthorizedClientProvider
refreshes tokens only if the access token expired which leaves room for expired ID token in the security context.To Reproduce Using an authorization server refreshing ID token as part of the refresh token flow (Keyckloak is one, and, according to the Stackoverflow question linked below, Spring Authorization Server seems to be another):
oauth2Client
configured withoauth2Login
and RP-Initated Logout(Reactive)OAuth2AuthorizedClientManager
to retrieve the access token - like Spring Cloud GatewayTokenRelay=
filter does - is enough.POST
request to the/logout
endpoint)Expected behavior
clockSkew
)OidcUser
) and if the refresh token response contains an ID token, then the ID token in the security context should be updatedend_session_endpoint
should be built with the last issued ID tokenThe
OidcClientInitiated(Server)LogoutSuccessHandler
currently uses the(Reactive)ClientRegistrationRepository
which doesn't trigger the refresh token flow in case of expired tokens. Shouldn't it use the(Reactive)OAuth2AuthorizedClientManager
instead?Some StackOverflow questions related to this issue: