Description
A flaw was found in Keycloak's refresh token rotation validation. When revokeRefreshToken is enabled and persistent session storage is in use, the stale token detection logic includes a condition that compares the cluster startup time against the last refresh timestamp. After a server restart, the cluster startup time is reset to the current time, which causes the stale token check to be skipped for all refresh tokens that were rotated before the restart. This allows a previously rotated (revoked) refresh token to be accepted and exchanged for a new access token.
Version affected
All versions with revokeRefreshToken support up to and including latest.
Expected behavior
Rotated refresh tokens should be rejected regardless of whether a server restart has occurred, as long as the session data is persisted and available.
Actual behavior
After a server restart, the stale token validation is skipped because the new startup time is later than the stored last-refresh timestamp, allowing rotated tokens to be reused.
Steps to reproduce
- Configure a realm with revokeRefreshToken enabled and persistent session storage.
- Authenticate a user and obtain a refresh token, then use it to refresh and obtain a new token. The original token is now rotated.
- Verify the original refresh token is rejected.
- Restart the Keycloak server.
- Attempt to use the original (rotated) refresh token again. Observe that it is accepted.
This issue was originally tracked in the private repository. Migrated by @abstractj.
Description
A flaw was found in Keycloak's refresh token rotation validation. When revokeRefreshToken is enabled and persistent session storage is in use, the stale token detection logic includes a condition that compares the cluster startup time against the last refresh timestamp. After a server restart, the cluster startup time is reset to the current time, which causes the stale token check to be skipped for all refresh tokens that were rotated before the restart. This allows a previously rotated (revoked) refresh token to be accepted and exchanged for a new access token.
Version affected
All versions with revokeRefreshToken support up to and including latest.
Expected behavior
Rotated refresh tokens should be rejected regardless of whether a server restart has occurred, as long as the session data is persisted and available.
Actual behavior
After a server restart, the stale token validation is skipped because the new startup time is later than the stored last-refresh timestamp, allowing rotated tokens to be reused.
Steps to reproduce
This issue was originally tracked in the private repository. Migrated by @abstractj.