Repository navigation
fix(auth): name the user a re-check blanked when its 401 ends the session - #1948
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request refactors the authentication store to track the last confirmed app user (heldAppUser) through session checks and sweeps, ensuring correct user information is announced upon sign-out. It introduces a userIdentity helper function and updates associated tests to cover these scenarios. The feedback suggests adding a fallback to the userIdentity helper to prevent returning undefined at runtime if user.username is missing, which could otherwise bypass null checks and cause unexpected serialization in localStorage.
Coverage Report
File Coverage
|
||||||||||||||||||||||||||||||||||||||
dawsontoth
marked this pull request as ready for review
October 13, 2026 18:01
…sion A tab's own `reloadUser` blanks the app user before asking central manager, so when that check came back 401 the announcement named nobody and the other tabs kept polling until their 403. The same happened to the sweep's user once a failed `/Logout/` was followed by a sign-in as someone else, because the remembered user was only the swept one. The store now keeps the last app user it held until a sign-out announces them, or a follower drops them, and announces nothing when it has no one to name. A page load whose first check 401s still names no one: it never knew its user. The mounted test's no-request and no-toast assertions now run after the redirect and a further poll, and two narrating comments are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`userIdentity` returned whatever the user's `id` or `username` held. A user record with neither, or with a non-string or empty id, therefore produced `undefined` or a number. That slipped past the announcement's null check and was written as an announcement naming nobody, and a tab holding such a user would follow any announcement that also named nobody. It now returns a non-empty string or null. Raised by gemini-code-assist on #1948. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dawsontoth
force-pushed
the
claude/cross-tab-sign-out-followup
branch
from
October 13, 2026 18:19
5fb07be to
7041206
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.
Problem
This follows up #1936, which is merged and deployed to stage. #1936 makes each tab follow a sign-out by writing an announcement,
Studio:AppSignOut, that names the user whose session ended.The last cross-model round on #1936 finished after the merge. It found sign-outs that named the wrong user, or no one:
reloadUserblanks the app user before it asks central manager. When that check came back 401,signOutAllLocallyhad no user to name. Other tabs ignored the announcement and kept polling until their next 403 toasted, which is the bug fix(auth): sign out every tab when one tab signs out #1936 fixes./Logout/leaves a stale user. The sweep remembered only the user it cleared. If/Logout/failed and the tab then signed in as someone else, a later 401 named the old user. Tabs signed in as the new user never heard.Solution
signOutAllLocallyannounces that user and forgets them. A follower drops them too, so a later 401 there cannot name them again. This replaces the record that only the sweep kept.Verification
70412063e, rebased onto currentstage,vitest run --maxWorkers=4exit 0 (535 files, 5,321 passed);tsc -b,oxlintanddprint checkexit 0. The husky hook's full run passed on every commit.Mutation checks
I reverted each guard locally and confirmed that a test goes red:
/Logout/unit testid/usernamevalue, not only a non-empty stringThe only other change is a removed narrating comment in the handler test.
Cross-model review
Round 1 (
20115cfa6). Reviewers: codexgpt-5.6-sol(graded), gemini and Harper domain adjudication;independent=true. The Cursor lens timed out. Verdict: LGTM, adjudicated severity nit.heldAppUserfield, now5fb07be22./Logout/test had changed. It hadn't; that hunk edits the next test.'*'generation bump makesloadUserreturn first.gemini-code-assist on this PR (one thread):
userIdentitycould returnundefined. I took it and widened it to non-string and empty ids, fixed in70412063e. I replied on the thread, pushed, then resolved it.Round 2 (
84d302940, the gemini fix). Reviewers: codex, gemini, cursor-muse and Harper domain adjudication;independent=true. Verdict: LGTM, adjudicated severity nit. The nit was that the field comment and DESIGN.md wording were imprecise (local Studio, the follower clear, and an undefined "U"). I fixed it in a comment-only amend; that amend and a rebase with no conflicts make the head70412063e.#1946 tracks the pre-existing finding from #1936's last round:
localStorage.clear()erases the explorer-epoch bump.🤖 Generated with Claude Code
Related PRs: #1936 overlaps (merged; this PR fixes what its last review round found after the merge)
Review-Coverage: authored=claude; ran=codex,gemini,cursor-muse; adjudicated=domain; blocked=cursor-grok(failed),cursor-composer(out-of-budget); declined=cursor-kimi,muse; rounds=2; full=1 @ 7041206
Review-Attention: study ~10m (sensitive: authStore.ts; decisions: capture-on-every-write, name-not-session-matching) @ 7041206