Cloud API scopes the active org by the URL selector header#999
Merged
Conversation
Deploying with
|
| 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 |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
executor-cloud | be7f2df | Jun 13 2026, 05:47 PM |
Contributor
Cloudflare previewTorn down — the PR is closed. |
@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
2ac5e0a to
ea800cf
Compare
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.
17798ec to
be7f2df
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 #974.
What
The cloud API resolves the active organization from the request's org selector (
x-executor-organizationheader) 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.ts—orgSelectorFromRequest+authorizeOrganizationSelector(slug → org → live-membership check; org-id → live-membership check).auth/workos-auth-provider.ts—resolveSessionPrincipalprefers the selector, falls back to the session org.account/workos-account-service.ts—me/requireOrganizationand every account method thread the headers through the same selector precedence.OrgAuthLivestays session-scoped (HttpApiMiddleware security handlers can't carry theUserStoreServiceresidual 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).