Skip to content

fix(security): apply token-store operations in the order they are issued - #1001

Merged
koniz-dev merged 2 commits into
mainfrom
fix/token-store-serialized
Oct 30, 2026
Merged

koniz-dev merged 2 commits into
mainfrom
fix/token-store-serialized

Conversation

@koniz-dev

@koniz-dev koniz-dev commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

The session guards in AuthRepositoryImpl.refreshToken() and AuthInterceptor check 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 before localStorage.setItem, but delete/deleteAll call removeItem at once. So the clear landed first, and the write then re-stored a live access token on a signed-out browser. AuthInterceptor.onRequest would attach that token to the next request.

Change. SecureTokenStore runs 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 through tokenStoreProvider: AuthRepositoryImpl.logout() and AuthInterceptor._logoutUser(), which both call clearAllTokens(). 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 in refreshToken() now describes the store guarantee instead of an open window.

Tests. test/core/storage/adapters/secure_token_store_order_test.dart runs over a storage that reorders like the web plugin: writes land after an async step, deletes land at once.

  • A clearAllTokens() issued after a pending setAccessToken() leaves nothing. This is the property _logoutUser() relies on.
  • logout() during refreshToken()'s access-token write leaves both tokens null, and the refresh reports SESSION_TERMINATED.
  • login() completing after the stale write keeps login-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 qa subagent 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:

Refs #986

🤖 Generated with Claude Code

koniz-dev and others added 2 commits October 31, 2026 03:35
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant