CL-6394: route OAuth connects through the tenant-scoped mount; one shared persist-and-seed sequence - #149
Merged
Conversation
…OAuth mount The chain CL-6394 traced: the plugins gallery's Connect link targeted /api/onboarding/oauth/github/start, whose mount dispatches only openrouter/huggingface and crashed a github callback after a successful token exchange (CATALOG_SEEDS["github"] is undefined). These tests pin the fixed contract: the UI's start URL targets the tenant-scoped connections/oauth mount, a github callback through that exact URL persists the exchanged token (fake exchange server) without ever touching the catalog seeds, the onboarding mount answers a loud 404 for github, and the shared persist-and-seed helper decides catalog seeding in one place.
…ount CL-6394: the hosted GitHub one-click connect crashed in its callback — the UI targeted onboarding's OAuth mount, which only knows openrouter/huggingface, while the correct tenant-scoped connections/oauth mount had zero frontend consumers. - oauthStartHref now emits /api/tenants/:id/connections/oauth/:connector/start; the settings Connections cards and the plugins gallery both ride it. Onboarding's own mount keeps only its first-login OpenRouter/Hugging Face flow: its registry no longer spreads CONNECTOR_REGISTRY, so any other connector 404s loudly, and its persistence narrows the connector id instead of casting it. - createOAuthConnectRoutes takes a typed env parameter, so the tenant-scoped connectCredential reads tenant/principal off Context<TenantEnv> with no cast. - One shared persistConnectorCredential replaces the three parallel ensureProvider -> ensureCredential -> seedCatalog copies (connections /complete, onboarding's testAndPersistCredential, the tenant OAuth connect); only inference providers ever reach seedCatalog, and seedCatalog now accepts the already-planted credential so the one ensureCredential write stays the only one. Credential metadata (token expiry) now survives the tenant OAuth path too. - ConnectStatePayload accepts an empty codeVerifier — the non-PKCE GitHub flow seals one, and the old 'string > 0' silently expired every such callback. - The plugins gallery's not-connected rows use the same single Connect verb as the MCP preset rows instead of a bare plus glyph.
.env.example now names the tenant-scoped github/huggingface callback shapes the connect surfaces actually drive (CL-6394) and drops the stale not-yet-mounted note. Also removes the .app-mark/.app-wordmark rules nothing renders anymore.
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.
Fixes the hosted GitHub one-click connect crash traced in CL-6394: the only UI entry point targeted
/api/onboarding/oauth/github/start, whose mount dispatches only openrouter/huggingface — a github callback fell into inference-only seeding and hitCATALOG_SEEDS["github"](undefined) AFTER a successful token exchange, while the correct tenant-scoped mount from #136 had zero frontend consumers.What changed
oauthStartHrefnow emits/api/tenants/:id/connections/oauth/:connector/start; both non-onboarding connect surfaces (settings Connections cards, plugins gallery panel) ride it. The onboarding wizard keeps its own mount for its first-login OpenRouter/Hugging Face flow only.CONNECTOR_REGISTRYspread), sooauth/github/startthere is a loud 404; persistence/recovery/pending-seed narrow the connector id explicitly instead ofas SupportedCredentialProvider.createOAuthConnectRoutes<E extends AppEnv>threads the env throughconnectCredential/afterConnected, socreateTenantConnectCredentialreadstenant/principaloffContext<TenantEnv>with no cast.persistConnectorCredential(@workbench/connections/persist-credential) replaces the three parallel ensureProvider→ensureCredential→seedCatalog copies (connections/complete, onboardingtestAndPersistCredential, tenant OAuth connect). Only inference providers reachseedCatalog; it now takesexistingCredentialIdso the caller's singleensureCredentialstays the only credential write, and CL-6351'shasCompletionCapableModelguidance still flows back through/complete. Credential metadata (HF token expiry) now survives the tenant OAuth path too.ConnectStatePayloadrequired a non-emptycodeVerifier, but GitHub's non-PKCE flow seals an empty one — every github callback would have been reportedstate_expiredeven with the routing fixed. Now accepts the empty string..app-mark/.app-wordmarkCSS deleted;.env.examplecallback-URL notes updated (stale "not yet mounted" note dropped).Red/green
packages/connections/test/github-oauth-connect.test.tsdrives the exact start URL the UI emits through start → callback with a fake GitHub token exchange server: credential persisted, no catalog seed touched.oauth-start-href.test.tspins the same literal on the UI side; the onboarding suite pins the github 404.Notes
<BASE_URL>/api/tenants/); OpenRouter accepts anycallback_url. Hugging Face registers exact redirect URIs — the tenant-scoped callback shape may need its own registration; flagged in.env.exampleand worth a follow-up check against a real HF app.origin/main, not touched here: 4 flakyapps/webtoast tests (fail under the full parallel suite, pass in isolation; identical with this branch's changes stashed), theapps/websonnertsc resolution error, and theworkflow-catalognon-null-assertion lint error.