Skip to content

Cloud API scopes the active org by the URL selector header#999

Merged
RhysSullivan merged 1 commit into
mainfrom
claude/stateless-org-server
Jun 13, 2026
Merged

Cloud API scopes the active org by the URL selector header#999
RhysSullivan merged 1 commit into
mainfrom
claude/stateless-org-server

Conversation

@RhysSullivan

Copy link
Copy Markdown
Owner

Stacked on #974.

What

The cloud API resolves the active organization from the request's org selector (x-executor-organization header) instead of relying solely on the org pinned into the WorkOS sealed-session cookie. The selector accepts either an org slug or a WorkOS org id; the session org remains the fallback for callers that send no header (non-console clients, server-to-server).

Crucially, the selector is not a trust boundary — exactly like the MCP plane's URL-pinned org. Live WorkOS membership is re-checked on every request, so a selector for an org the caller isn't a member of resolves to no organization, never to access.

Why

This is the server half of moving cloud off cookie-based "active org" to stateless, URL-scoped orgs. The session authenticates the user to all their orgs at once; the URL (carried as the selector header) decides which one a given request acts on. That removes the single shared "active org" — the thing that makes two browser tabs on different orgs corrupt each other.

Shape

  • auth/organization.tsorgSelectorFromRequest + authorizeOrganizationSelector (slug → org → live-membership check; org-id → live-membership check).
  • auth/workos-auth-provider.tsresolveSessionPrincipal prefers the selector, falls back to the session org.
  • account/workos-account-service.tsme/requireOrganization and every account method thread the headers through the same selector precedence.
  • OrgAuthLive stays session-scoped (HttpApiMiddleware security handlers can't carry the UserStoreService residual a slug lookup needs) — noted inline.

Tests

auth/org-selector-auth.node.test.ts — selector precedence: session fallback, slug wins over session org, org-id accepted, and a non-member selector is rejected (NoOrganization).

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
executor-marketing be7f2df Commit Preview URL

Branch Preview URL
Jun 13 2026, 05:46 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 13, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
executor-cloud be7f2df Jun 13 2026, 05:47 PM

@github-actions

github-actions Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Cloudflare preview

Torn down — the PR is closed.

@pkg-pr-new

pkg-pr-new Bot commented Jun 13, 2026

Copy link
Copy Markdown

Open in StackBlitz

@executor-js/cli

npm i https://pkg.pr.new/@executor-js/cli@999

@executor-js/config

npm i https://pkg.pr.new/@executor-js/config@999

@executor-js/execution

npm i https://pkg.pr.new/@executor-js/execution@999

@executor-js/sdk

npm i https://pkg.pr.new/@executor-js/sdk@999

@executor-js/codemode-core

npm i https://pkg.pr.new/@executor-js/codemode-core@999

@executor-js/runtime-quickjs

npm i https://pkg.pr.new/@executor-js/runtime-quickjs@999

@executor-js/plugin-file-secrets

npm i https://pkg.pr.new/@executor-js/plugin-file-secrets@999

@executor-js/plugin-graphql

npm i https://pkg.pr.new/@executor-js/plugin-graphql@999

@executor-js/plugin-keychain

npm i https://pkg.pr.new/@executor-js/plugin-keychain@999

@executor-js/plugin-mcp

npm i https://pkg.pr.new/@executor-js/plugin-mcp@999

@executor-js/plugin-onepassword

npm i https://pkg.pr.new/@executor-js/plugin-onepassword@999

@executor-js/plugin-openapi

npm i https://pkg.pr.new/@executor-js/plugin-openapi@999

executor

npm i https://pkg.pr.new/executor@999

commit: 17798ec

@RhysSullivan RhysSullivan force-pushed the claude/stateless-org-server branch from 8bf8a7f to 17798ec Compare June 13, 2026 17:40
@RhysSullivan RhysSullivan force-pushed the claude/sharp-shirley-295497 branch from 2ac5e0a to ea800cf Compare June 13, 2026 17:40
@RhysSullivan RhysSullivan changed the base branch from claude/sharp-shirley-295497 to main June 13, 2026 17:44
Org-scoped requests carry the active org in an x-executor-organization
header (a slug or org_ id); the server resolves it against live WorkOS
membership and scopes to it, falling back to the session's own org when
absent. Additive and backward-compatible — existing cookie-only callers
keep working via the fallback. The header is a selector, not a trust
boundary (membership is always re-checked), mirroring the MCP plane.

This is the server half of moving off cookie-based 'active org': once
the web client sends its URL's org per request, two browser tabs on
different orgs scope independently instead of fighting over one shared
cookie.

Covers the executor data plane (workos-auth-provider) and the account
plane (members/api-keys/me). The domains plane stays session-scoped: its
HttpApiMiddleware security handler may carry no residual requirement, so
it can't reach the per-request UserStoreService a slug needs — noted for
a follow-up that converts it to an HttpRouter middleware.
@RhysSullivan RhysSullivan force-pushed the claude/stateless-org-server branch from 17798ec to be7f2df Compare June 13, 2026 17:45
@RhysSullivan RhysSullivan merged commit 58e23e8 into main Jun 13, 2026
10 of 12 checks passed
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