Web clients scope by the URL org; drop cookie-based org switching#1000
Merged
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-marketing | 600d7e1 | Commit Preview URL Branch Preview URL |
Jun 13 2026, 05:49 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | 600d7e1 | Jun 13 2026, 05:50 PM |
@executor-js/cli
@executor-js/config
@executor-js/execution
@executor-js/sdk
@executor-js/codemode-core
@executor-js/runtime-quickjs
@executor-js/plugin-file-secrets
@executor-js/plugin-graphql
@executor-js/plugin-keychain
@executor-js/plugin-mcp
@executor-js/plugin-onepassword
@executor-js/plugin-openapi
executor
commit: |
8bf8a7f to
17798ec
Compare
d78ec37 to
560aa7d
Compare
Contributor
Cloudflare previewTorn down — the PR is closed. |
17798ec to
be7f2df
Compare
The console URL's org slug is now the request scope on the client too: both API clients (core + account) read the slug from window.location at request time and send it as the x-executor-organization header, so every tab's requests are scoped to the org its own URL names — independent of the single shared session cookie. With requests URL-scoped, the cookie-based switch machinery is dead weight: - delete the switchOrganization endpoint/handler/atom; the org switcher and create-org just navigate to /<slug> (the session already authenticates the user to all their orgs, so there is nothing to switch server-side). - delete ForeignOrgSlug; a slug the caller can't access now resolves to a null org via /account/me and the shell 404s, no switch-into-org dance. - simplify OrgSlugGate to canonicalize a bare URL onto the active slug and otherwise render through (a slug in the URL is already the scope). Flips the multi-tab scenario from reproducing the cookie-steal corruption to asserting per-tab independence, and reframes the foreign-slug scenario around URL scoping (no session switch; the cookie is never rewritten).
560aa7d to
600d7e1
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.
Stacked on #999 (which is stacked on #974).
What
The client half of stateless URL-scoped orgs. Both API clients (core + account) read the org slug from
window.locationat request time and send it as thex-executor-organizationheader, so every request is scoped to the org its own tab's URL names. With requests URL-scoped, the cookie-based switch machinery becomes dead weight and is deleted:switchOrganizationendpoint + handler + atom — gone. The org switcher and create-org dialog just navigate to/<slug>; the session already authenticates the user to all their orgs, so there's nothing to switch server-side.ForeignOrgSlug— gone. A slug the caller can't access now resolves to a null org via/account/me, and the shell 404s — no switch-into-org dance, no cookie rotation.OrgSlugGate— reduced to its one remaining job: canonicalize a bare URL onto the active slug; a slug already in the URL is the scope, so it renders through.The slug is read from
window.location(not a React-synced mirror) deliberately: an earlier attempt to mirror it into a module global during render caused an SSR/hydration mismatch and a stuck onboarding redirect. Reading at request time in the browser-onlytransformClientavoids that entirely.Why
WorkOS pins one org into the shared
wos-sessioncookie, and the whole browser shares one cookie jar — so under the old model "active organization" was a browser-global. Two tabs couldn't be in two orgs at once: a switch in either tab (or the gate's switch-to-honor-the-URL) silently re-scoped the other tab's session. The URL is now the scope, so the tabs are independent.Evidence
Cloud e2e (real browser), all green:
org-multitab-cookie— flipped from reproducing the cookie-steal corruption to asserting per-tab independence. Two tabs in oneBrowserContext(one cookie jar) on different orgs; reads the real outgoingx-executor-organizationheader off each tab's requests. Tab 2 opening org A no longer re-scopes tab 1 — tab 1's requests stay pinned to org B.org-slug-foreign— opening another of your orgs by its slug works with the session cookie never rewritten (no switch); proves the URL alone is the scope.org-switcher,org-slug-routing— still pass (switcher now navigates by URL; unknown/unauthorized slug → 404).Gates: typecheck 39/39, lint clean, format clean, react 153 tests, cloud 125 tests.
A recording of the multi-tab independence run is available locally (
session.mp4); I can embed it here on request.