Skip to content

Sync upstream while preserving external OIDC login - #6

Merged
zrm625 merged 331 commits into
mainfrom
chore/upstream-sync-2026-09-05
Sep 5, 2026
Merged

zrm625 merged 331 commits into
mainfrom
chore/upstream-sync-2026-09-05

Conversation

@zrm625

@zrm625 zrm625 commented Sep 5, 2026 •

Copy link
Copy Markdown
Owner

Merge UsefulSoftwareCo/executor at 38915a32cfa0fa1fd0012ee12a8c21716710dbc1 into this fork, retaining opt-in external OIDC alongside upstream SSO. This includes 328 upstream commits; dependency manifests, all seven patches, and bun.lock remain identical to upstream.

Upgrade requirement: existing external OIDC links must be relinked. Old records contain only a subject and cannot establish which issuer owned it. They remain stored but cannot authenticate automatically. Users sign in with their local password and explicitly relink with a matching verified provider email. New links bind issuer and subject; switching issuers cannot reuse another issuer's account. No automatic database rewrite or deletion is performed. Qualify local account access before upgrading. A source downgrade can reactivate legacy links and cannot safely be combined with an issuer change without separate rollback qualification.

Both providers share one generic OAuth plugin. External OIDC retains explicit local-account proof and rejects signup or implicit email matching. SSO retains verified-domain admission and offers authenticated linking for existing setup/invite accounts without database edits or relaxed provider/email checks. Provider failures return to usable login with ordinary deep links and MCP authorization parameters intact. Partial SSO configuration fails at boot; external client secrets accept opaque provider-issued values while retaining secret-file ownership and permission checks.

The selfhost Vite HTTP adapter now preserves every Set-Cookie header. Its inherited overwrite behavior dropped the session cookie during interrupted MCP login. Emulator/browser setup releases resources on acquisition failures as well as normal completion.

Validation:

  • Bun 1.3.11 bootstrap/frozen install, formatting, lint, generated routes, and patch sentinels passed.
  • All 45 typecheck tasks and 39 native test tasks passed, including 179 selfhost tests; e2e typecheck passed.
  • Published local Okta emulator tests prove independent provider policies, legacy-record relinking, same-subject issuer-switch denial, and original-issuer restart compatibility.
  • Narrow browser coverage passes MCP registration → SSO rejection → local login → consent approval → PKCE token exchange, plus provider coexistence, explicit linking, deep-link return, and disabled controls. An intentional boot failure proves the emulator's exact port can be reacquired. Standard screenshots/video/trace are under e2e/runs/selfhost/external-oidc-coexistence-explicit-linking-and-deep-link-return/.
  • The MCP browser failure was reproduced with the auth implementation restored exactly from upstream before fixing the Vite transport. No production deployment, live account migration, or database downgrade qualification was performed.

Merge with a merge commit to retain upstream ancestry; do not squash or rebase.

RhysSullivan and others added 30 commits August 17, 2026 12:38
…o#1626)

* Bind span header redaction to the hosted HTTP client

Live verification after the telemetry-layer redaction shipped showed
post-deploy http.client spans still recording non-allowlisted headers:
consumers capture the client value at construction and execute requests on
fibers a host telemetry layer never reaches, so providing the redaction
reference by layer was not enough. Bind it to the client itself so every
request effect carries it, and keep the cloud layer as the server-side half.
Regression test drives a request through the real client and asserts on the
recorded span attributes.

* Add DOM.Iterable to the mcp plugin tsconfig

The package's tsc build failed on Headers.entries() and the iterable
protocol: its lib had DOM without DOM.Iterable, so the fetch Headers type
lacked both. Latent on main; every CI run served the package build from the
turbo cache until the sdk change invalidated it. Same lib pairing
hosts/cloudflare already uses.
* Add first-party OAuth clients (host-operated GitHub/Google apps)

* Allow endpoint overrides for the first-party GitHub app in tests/dev

* Launch built-in Google OAuth for Calendar and Sheets
…uting (UsefulSoftwareCo#1632)

* Send keepalive comments on rotated MCP SSE streams

* Route drained Last-Event-ID reconnects to the standalone listener

* Resume reconnects for streams with in-flight requests
* Speed up selfhost e2e: tunable sandbox deadline + 3-way sharding

* Fix the local e2e suite: toolkit MCP DB lock, Bun-only spawn, stale auth selector

* Stabilize CI process lifecycle
* Cut CI toward two minutes

* Bring CI under two minutes

* Make approval lease test deterministic

* Stabilize E2E dependency setup

* Harden CI regression coverage
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…sefulSoftwareCo#1656)

* Fix two CI e2e flakes: stylesheet-count race and dev-db queue wedge

* Raise sandbox-heavy discovery-shape test to the file's 10s ceiling
UTM-tagged landings on executor.sh produced no $pageview, so campaigns read
as zero traffic in PostHog. The params only reached $web_vitals, which no
funnel or web-analytics view uses. Autocapture stays off.
…finity (UsefulSoftwareCo#1658)

* Retry reveal-style clicks that race console hydration

* Repair dev-db queue affinity abandoned by mid-execution disconnects
RhysSullivan and others added 19 commits August 29, 2026 22:04
* run independent tool-call reads concurrently

* add e2e coverage for the tool-call contract

* interrupt speculative reads on branches that never consume them

* launch the dominant read before the speculative forks start

* make dominant-first read launch structural, not scheduling-dependent

* Accept the rare budget-yield launch reversal instead of suppressing yields

* State the real launch-order guarantee in the fork comment
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* desktop publish: blacksmith runners, bun/electron caches, dry-run input

* desktop publish: parallelize release asset uploads

* desktop publish: drop caches, cold install is faster on these runners
* Overlap desktop build with npm publishes and parallelize package publishing

* Gate desktop promote on the exact publish run; validate variant set from wrapper optionalDependencies
* Support raw OAuth Basic credentials

* Fix OAuth app E2E selector

* Stabilize cloud session cap E2E
* Restrict workspace writes to admins

Co-authored-by: Max schwenk <maschwenk@gmail.com>

* Fix self-host MCP request forwarding

* Update file secrets durability invariant

* Fix versioned cloud credentials

* Fix self-host live-role scenario

* Update cloud principal contract tests

* Remove stray audit history assertions

* Fix no-auth credential classification

* fix(mcp): project legacy stdio as no-auth

* fix(graphql): classify empty auth templates as no-auth

* fix(sdk): defer no-auth classification to engine

* fix(sdk): ignore placements on no-auth methods

* test(mcp): make reconcile cleanup infallible

* test(graphql): use empty no-auth timeout input

* test(e2e): remove no-auth placeholder credentials

* test(e2e): use empty no-auth credential shapes

* Project legacy stdio credentials truthfully

* Reject invalid no-auth placements

* Reject MCP no-auth credential input

* Propagate MCP test cleanup failures

* Honor projected legacy stdio credentials

* Deduplicate invalid auth method warnings

---------

Co-authored-by: Max schwenk <maschwenk@gmail.com>
* feat(host-selfhost): Google sign-in with domain-allowlist admission

* feat(host-selfhost): generic OIDC SSO sign-in with verified-domain admission

* refactor(host-selfhost): extract SSO admission helpers, trim comments
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-05T14:12:46.975056Z 0cd86af Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fa485591b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return {
context: {
context: {
options: { account: { accountLinking: { disableImplicitLinking: false } } },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Provide a usable path for verified SSO linking

When SSO is enabled on an existing instance, users created through setup or invite-based email signup cannot use this implicit-linking branch: emailAndPassword configures no email-verification flow, so those users remain unverified, while Better Auth requires the local email to be verified before linking. The added test only succeeds after manually setting emailVerified = 1 in SQLite, and the UI exposes explicit linking only for external-oidc, not the configured SSO provider. Consequently, a normal existing user with a matching IdP email cannot adopt SSO without an operator editing the database; add an authenticated SSO-link flow or a reachable local email-verification path.

Useful? React with 👍 / 👎.

// way it does for the email form's post-login navigation.
const result = await authClient.signIn.oauth2({
providerId,
callbackURL: postLogin,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Return failed SSO callbacks to the login page

When the IdP denies authorization or the callback rejects a user outside EXECUTOR_SSO_ALLOWED_DOMAINS, this request stores no errorCallbackURL, so Better Auth sends the browser to its default auth-error endpoint instead of returning to the self-host login UI. The external-OIDC paths in this component explicitly supply an error callback for exactly this case, while the SSO test only checks that the resulting redirect contains error and never follows it. Supply a login-page error callback that also preserves postLogin so users can recover or choose local sign-in without landing on a raw auth endpoint.

Useful? React with 👍 / 👎.

@zrm625

zrm625 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

providerId: oidc.providerId,

P1 Badge Scope linked account identifiers to the issuer

When an operator changes EXECUTOR_OIDC_ISSUER, this fixed provider ID causes Better Auth to look up both the old and new providers' accounts under the same (providerId, sub) namespace. If the new issuer returns a subject already linked under the previous issuer, an account at the new IdP can sign in as that existing Executor user without going through the email-matching link flow. Include the issuer in the persisted account identity or invalidate existing links when the issuer changes.


const oidcErrorCallback = `/login?error=oidc&returnTo=${encodeURIComponent(postLogin)}`;

P2 Badge Preserve MCP authorization across callback errors

When this page is serving an interrupted MCP authorization, postLogin is /api/auth/mcp/authorize?..., so this new error URL nests that value inside returnTo. After an SSO or external-OIDC failure returns to /login, postLoginTarget rejects the nested target because safeReturnTo excludes /api/*; retrying local or provider sign-in therefore goes to / while the MCP client remains waiting. The fresh evidence beyond the handled ordinary deep-link case is this MCP-specific target produced by mcpAuthorizeResumeTarget; preserve the original top-level authorization parameters or explicitly validate and resume this internal endpoint.


if (!EXTERNAL_OIDC_CLIENT_SECRET_PATTERN.test(direct)) {
// oxlint-disable-next-line executor/no-try-catch-or-throw, executor/no-error-constructor -- boundary: reject weak or ambiguously encoded confidential-client credentials before auth starts
throw new Error("EXECUTOR_OIDC_CLIENT_SECRET must be exactly 64 base64url characters");
}

P1 Badge Accept provider-issued OIDC client secrets

When an IdP issues an opaque client secret that is not exactly 64 base64url characters, enabling external OIDC now throws during boot even though the credential is valid for that provider. OIDC providers control this value and commonly vary its length and character set, so operators cannot necessarily regenerate it in this imposed format; the tests conceal the incompatibility by using "A".repeat(64). Treat the nonempty provider-issued secret as opaque rather than enforcing a locally generated-key format.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +33 to +36
const idp = await createEmulator({
service: "okta",
port: ports.E2E_SELFHOST_SSO_IDP_PORT!,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Close the emulator when setup aborts

If client registration, user seeding, or bootSelfhost fails after this acquisition, the callback never returns its teardown, and the later Effect.ensuring is never installed because booted was not produced. The emulator therefore retains its listening port and can keep the failing e2e worker alive; wrap the entire acquisition sequence in cleanup that closes idp on every setup failure.

AGENTS.md reference: e2e/AGENTS.md:L16-L17

Useful? React with 👍 / 👎.

Comment thread apps/host-selfhost/src/config.ts Outdated
Comment on lines +365 to +367
const clientId = process.env.EXECUTOR_SSO_CLIENT_ID?.trim();
const clientSecret = process.env.EXECUTOR_SSO_CLIENT_SECRET?.trim();
if (!clientId && !clientSecret) return undefined;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject all partial SSO configurations

When an operator sets SSO metadata such as EXECUTOR_SSO_PROVIDER_ID, EXECUTOR_SSO_DISCOVERY_URL, or EXECUTOR_SSO_ALLOWED_DOMAINS but the client credential injection is missing, this early return silently disables SSO instead of reporting the incomplete deployment configuration. That contradicts the surrounding fail-at-boot contract and leaves the instance running local-only with no indication beyond the missing login button; determine whether any SSO variable is present before returning undefined.

Useful? React with 👍 / 👎.

@zrm625

zrm625 commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 0cd86af988

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@zrm625
zrm625 merged commit 1f10adf into main Sep 5, 2026
1 of 37 checks passed
@zrm625
zrm625 deleted the chore/upstream-sync-2026-09-05 branch October 16, 2026 17:38
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.