Repository navigation
fix(security): apply token-store operations in the order they are issued - #1001
Merged
Merged
Conversation
On web, flutter_secure_storage awaits WebCrypto encryption before a write reaches localStorage but removes a key at once, so a logout's clear, issued after a token refresh had checked the session and started writing, landed first, and the write then left a live access token on a signed-out browser. SecureTokenStore now queues every read, write and delete behind the ones issued before it, so the order the session guards rely on holds on every platform. A login that completes after a stale write still keeps its own tokens, since nothing clears on a stale return. Refs #986 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…queue's cost Refs #986 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
koniz-dev
added a commit
that referenced
this pull request
Oct 30, 2026
Refs #986 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
The session guards in
AuthRepositoryImpl.refreshToken()andAuthInterceptorcheck the session immediately before a token write. That only works if a logout's clear, issued after the check, also lands after the write. On web, flutter_secure_storage 2.1.1 awaits WebCrypto encryption beforelocalStorage.setItem, butdelete/deleteAllcallremoveItemat once. So the clear landed first, and the write then re-stored a live access token on a signed-out browser.AuthInterceptor.onRequestwould attach that token to the next request.Change.
SecureTokenStoreruns every read, write and delete behind the operations issued before it, through a small queue that a failure does not wedge. Both logout paths share the one store throughtokenStoreProvider:AuthRepositoryImpl.logout()andAuthInterceptor._logoutUser(), which both callclearAllTokens(). So the order the guards rely on now holds on every platform. Nothing clears on a stale return, so a session that started meanwhile keeps its tokens. The comment inrefreshToken()now describes the store guarantee instead of an open window.Tests.
test/core/storage/adapters/secure_token_store_order_test.dartruns over a storage that reorders like the web plugin: writes land after an async step, deletes land at once.clearAllTokens()issued after a pendingsetAccessToken()leaves nothing. This is the property_logoutUser()relies on.logout()duringrefreshToken()'s access-token write leaves both tokens null, and the refresh reportsSESSION_TERMINATED.login()completing after the stale write keepslogin-access/login-refresh.Against the unqueued store, the first two fail.
Not verified (tier 3). Real WebCrypto timing in a browser.
Pre-merge review (a fresh
qasubagent on the diff): no blocking defect. It found no re-entrancy, and errors propagate without wedging the queue. The queue is null while idle, as the cache's index queue is. Its points:_logoutUser()only callsclearAllTokens()on the shared store, and the first test is exactly that ordering. No interceptor-level test is added.ITokenStore(second commit).SecureStorageService.clear()bypasses the queue. No caller is on the token path; filed as fix(storage): SecureStorageService.clear() bypasses the token store's ordering, so a logout built on it would reopen the web token race #1003 (P3).Refs #986
🤖 Generated with Claude Code