Keep identity tokens of parallel refreshes refreshable - #78
Merged
Merged
Conversation
The refresh token in the session is now bound to a generation of identity tokens: the token of the login, or all tokens which requests received by refreshing the same predecessor generation. Any token of the current generation can be refreshed, and requests with a token of the previous generation receive the latest one for ten minutes. Previously, the session remembered only the identity token which was stored last. Browsers send several requests in parallel, for example polls or fragment loads. If the identity token expired just before such a burst, several requests refreshed it at the same time. The session kept the result of the request which wrote last, while the browser could keep the cookie of another response. That token worked until it expired about an hour later. Its refresh was then rejected with "belongs to another identity token", and the user was logged out although the session was still valid. Before storing a refreshed identity token, the provider now reads the session data again. Flow reads session data from the storage on every call, so the provider sees what parallel requests stored while it waited for the identity provider. If another request refreshed the same generation meanwhile, the new token joins that generation and its refresh token replaces the stored one. After writing, the provider reads once more and repeats the merge if a parallel write replaced its token. If the session no longer accepts the expired token, for example after a logout or another login during the refresh, the refreshed token is discarded and the request is not authenticated. Only tokens which were issued for this session join a generation, so an older identity token together with a stolen session stays useless. Session data stored by version 6.0.0 is still accepted.
robertlemke
force-pushed
the
bugfix/parallel-refresh-logout
branch
from
September 15, 2026 12:45
920872b to
a677882
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
With version 6.0.0, users get logged out about an hour after their identity token was refreshed, although their session is still valid. The security log says that the refresh token in the session "belongs to another identity token". This should go into a 6.0.x patch release.
The cause is a race between parallel requests, for example HTMX polls or several tabs. If the identity token has just expired, each request refreshes it on its own and receives a different identity token. The session keeps the result of the request which wrote last, but the browser may keep the cookie of another one, and that token can't be refreshed an hour later.
The refresh token in the session is now bound to a generation of identity tokens: all tokens which requests received by refreshing the same predecessor. Any token of the current generation can be refreshed. Before storing a refreshed token, the provider reads the session again and joins a generation which a parallel request stored meanwhile, and it confirms the write by reading once more. If the session was logged out or logged in again during the refresh, the refreshed token is discarded. The security properties of 6.0 stay the same, and session data of 6.0.0 is still read.
Flow's session storage has no compare-and-set, so a small window for lost updates remains. With refresh token rotation, the reuse interval of the identity provider must cover such a burst of parallel refreshes.