Skip to content

fix(auth): preserve password replacement across concurrent 401s - #9607

Open
wunianze666-netizen wants to merge 3 commits into
invoke-ai:mainfrom
wunianze666-netizen:work/auth-epoch-401-race
Open

wunianze666-netizen wants to merge 3 commits into
invoke-ai:mainfrom
wunianze666-netizen:work/auth-epoch-401-race

Conversation

@wunianze666-netizen

@wunianze666-netizen wunianze666-netizen commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

User impact and scope

Password changes advance the backend token epoch and return a replacement in X-Refreshed-Token. A concurrent request made with the old token can receive 401 before the password response arrives. Logging out immediately destroys the session before the replacement can be committed. On current main, this affects both the legacy webv1 client and the default webv2 client. This is distinct from the routine-refresh-first race addressed by #9599.

Root cause and fix

  • webv1: mark profile password changes and admin self-resets as short-lived, cross-tab-visible operations; defer an old-token 401 until the matching operation settles; recheck the current token and account generation before logout. Preserve the existing replacement-acceptance path.
  • webv2: the HTTP transport previously ignored successful responses' replacement header. Pass it to the identity adapter, which accepts it only for the same account lifetime and rejects stale epoch refreshes. Publish the password-change marker before PATCH /auth/me or an admin self-reset; when an old-token 401 arrives first, defer expiry and recheck the live credential after the operation completes.
  • Markers contain a user/epoch key, never bearer bytes in persistent storage. They expire after 30 seconds and are cleaned up on completion. A failed change or genuinely expired token still triggers normal logout; account switches and explicit logout cannot be undone by a late response.

The bounded wait can delay a genuine 401 when the password-changing tab disappears, but cannot suppress it indefinitely. The implementation keeps token policy in Identity and passes only a narrow callback through Platform HTTP.

Red/green evidence

Before the fix, a controlled old-token-401-first request signed the user out and prevented the password response from installing its replacement. The new webv2 regression also failed because the success header was ignored. After the fix:

  • webv1: seven auth/API test files, 61 tests passed; targeted ESLint and Prettier passed. Full webv1 tsc --noEmit currently reports unrelated model-type fixture errors on the updated main, with no diagnostics in the five changed webv1 files.
  • webv2: identity plus HTTP transport tests, seven files / 61 tests passed, including 401-first, same-epoch-refresh-before-replacement, failed change, and admin self-reset cases. tsc --noEmit, targeted oxfmt/oxlint, secure-context and dependency architecture checks, git diff --check, and a production Vite build passed. The build reports existing chunk-size/dynamic-import warnings.
  • The new webv2 marker originally failed the repository's secure-context API guard because it minted a UUID directly; using the platform UUID helper made that guard pass.

The branch is updated through commits e724d69ee (carry the webv1 change across the frontend move) and dc6aa7176 (default webv2 fix). Full browser/release checks were not run locally; CI results should be considered separately.

Developed with Codex AI assistance. The checks above were run locally; no claim of upstream adoption is made until maintainer review and merge.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

frontend PRs that change frontend files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant