fix(web): don't strand a second account in the previous user's org - #5524
Open
pedrofrxncx wants to merge 1 commit into
Open
fix(web): don't strand a second account in the previous user's org#5524pedrofrxncx wants to merge 1 commit into
pedrofrxncx wants to merge 1 commit into
Conversation
pedrofrxncx
enabled auto-merge (squash)
July 31, 2026 19:39
pedrofrxncx
disabled auto-merge
July 31, 2026 19:44
`studio:last-org-slug` and `studio:last-location` are browser-global, so
signing in with a different account inherits the previous one's last org:
cold entry ("/") redirects there and dead-ends on "No access".
- Scope the restore state to a principal (`studio:last-user-id`): the shell
drops it as soon as the session resolves as somebody else, so the next "/"
resolves the new user's own default org. Also cleared on sign-out.
- Fix the gate's recovery. It bounced to "/" only when `lastOrgSlug` still
matched the org, but the redirect that lands users there now comes from
`lastLocation` — and `orgLayout.beforeLoad` overwrites that with the current
org on arrival, so it can't answer "did we send them here?" either. The home
loader now marks its restore redirects (per-tab sessionStorage) and the gate
reads that marker, so a restored org the user can't access sends them home
while a deliberate visit to a foreign org still shows the screen.
- Clear both keys (not just the slug) when landing on an archived org or
deleting the current one — `lastLocation` pointing at either bounced back.
Unit tests for the principal scoping and the marker (including that a
re-render gets the same answer, so StrictMode can't turn a bounce into the
dead-end screen). The `query-persist` test's window stub had to move to
`defineProperty`: happy-dom makes `window.localStorage` readonly, so the
plain assignment threw whenever another file registered it first.
pedrofrxncx
force-pushed
the
claude/fix-no-access-redirect
branch
from
July 31, 2026 19:52
76b79cb to
82eac49
Compare
pedrofrxncx
enabled auto-merge (squash)
July 31, 2026 19:52
Contributor
|
curious, instead of doing this: Couldn't we just remove that "lastUserId" and key by user id the others making me keep caches for all accounts i have? |
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
Signing in with a different account on a browser that already used Studio lands on No access instead of that account's own org:
studio:last-org-slugandstudio:last-locationare browser-global, but they describe one principal's history. A second account (or a session that expired and was replaced) inherits them, and cold entry (/) redirects straight into an org it can't access.The gate's existing self-heal didn't catch it: it bounced back to
/only whenlastOrgSlugstill matched the org, while the redirect that actually lands users there now comes fromlastLocation(checked first inhomeRoute) — andorgLayout.beforeLoadoverwriteslastLocationwith the current org on arrival, so it can't answer "did we send them here?" either.Changes
studio:last-user-idrecords who owns the two keys; the shell callsclaimRestoreStateFor(userId)as soon as the session resolves and wipes them if it's somebody else. Same pattern as the already user-scoped org cache (readCachedOrg). Sign-out clears them too (clearPersistedQueryCache, whose contract is already "next user starts clean").homeRoutemarks its restore-driven redirects (per-tabsessionStorage);OrgAccessGatereads the marker. A restored org the user can't access → back to/, which now resolves their default org. A deliberate visit to a foreign org (shared link) still shows the screen with a working "Go to home".lastLocationpointing at either bounced the user right back.Testing
apps/web/src/lib/last-location.test.ts— principal scoping, marker matching, and that a re-render gets the same marker answer (so StrictMode can't turn a bounce into the dead-end screen).bun test apps/web/src/lib/green (195 pass); fullbun test apps/webhas fewer failures thanmainin this environment (61 vs 68 — the pre-existing ones are unrelated).bun run fmt,bun run lint(0 errors),bun run checkpass.The
query-persisttest's window stub moved toObject.defineProperty: happy-dom makeswindow.localStoragea readonly accessor, so the plain assignment threw whenever another test file registered happy-dom first.Manual check
Sign in as A, visit an org only A can access, sign out, sign in as B → B lands on B's own org.
Summary by cubic
Fixes “No access” loops when signing in with a different account by scoping restore state to the current user and tracking restore-driven redirects. Also clears stale state on sign-out, org deletion, and archived orgs to avoid bouncing back.
studio:last-user-id;claimRestoreStateFor(userId)clears stalelastLocation/lastOrgSlugas soon as the session resolves. Sign-out also clears it viaclearPersistedQueryCache.homeRoutemarks restore redirects per-tab (markRestoreRedirect), andOrgAccessGateconsumes them (consumeRestoreRedirect) to send users back to/only for restore-driven arrivals without access; direct shared links still show the gate.clearRestoreState()when deleting an org or landing on an archived org.window.localStoragestubbing usingObject.defineProperty.Written for commit 82eac49. Summary will update on new commits.