Repository navigation
refactor(integrations): canonical provider key is the single integration identity - #719
Conversation
…nect Canva requires BOTH S256 PKCE and a client secret on token exchange, but /connect/oauth treated flow as pkce XOR confidential. PKCE is now an orthogonal usePkce switch (pkce=true|false query param, persisted in the integration record when it differs from the flow default), and a new basic-form token exchange style sends HTTP Basic client auth with an urlencoded body. api.canva.com defaults to confidential flow + PKCE + basic-form, mirroring the Notion basic-json host default.
Addresses CodeRabbit review on #716: percent-encode client_id and client_secret before joining with ':' and base64-encoding so reserved characters survive, and strengthen basic-form tests to cover body credential stripping and each validation condition independently.
Bugbot flagged that requiring usePkce in the sessionStorage config guard would reject configs persisted before the orthogonal-PKCE change, failing in-flight connects at the callback leg. Backfill the flow/host default instead of rejecting, and cover the legacy shapes with unit tests.
…ict validation The sessionStorage snapshot lives for a single authorize round trip, so a shape without usePkce can only exist for a flow in-flight across the one deploy that ships this change; recovery is restarting the connect flow. Keeping a permanent backfill for that transient window is not worth it. Validation now requires usePkce and rejects stale shapes deliberately.
…tegration identity Integration records were written under the raw provider string while the connect wizard probed both the raw name and the normalized key, and every other lookup (integration_get/delete, runtime helpers, OpenAPI bindings) was silently case-sensitive. Now canonicalIntegrationName (lowercase kebab via normalizeProviderKey) is applied by buildIntegrationValueName and normalizeIntegrationConfig, so every read and write path shares one key derivation; the client probe is deleted and parseIntegrationValueName only recognizes canonical keys. Kent's 17 stored integrations are all already canonical (verified via live audit), so no data migration is needed.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughIntegration identities are normalized to canonical lowercase-kebab provider keys across validation, saved values, parsing, OAuth lookup, documentation, and related test expectations. ChangesCanonical integration identity
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🔎 Preview deployed: https://kody-pr-719.kody-a99.workers.dev Worker: Mocks:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/worker/src/mcp/capabilities/integrations/integration-shared.ts`:
- Around line 17-22: Update integrationNameSchema’s refine predicate to require
at least one ASCII letter or digit in the canonicalized name, rather than only
checking that canonicalIntegrationName(name) is non-empty. Preserve the existing
minimum-length validation and error message so names consisting solely of dots,
underscores, or hyphens are rejected.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: cf5117b9-2a4c-4c45-85fd-d8a5882aec1b
📒 Files selected for processing (7)
docs/guides/oauth.mdpackages/worker/client/routes/connect-oauth.node.test.tspackages/worker/client/routes/connect-oauth.tsxpackages/worker/src/app/handlers/account-secrets.node.test.tspackages/worker/src/mcp/capabilities/integrations/integration-save.node.test.tspackages/worker/src/mcp/capabilities/integrations/integration-save.tspackages/worker/src/mcp/capabilities/integrations/integration-shared.ts
…n names Addresses CodeRabbit review on #719: names made only of dots, underscores, or hyphens survive canonicalization non-empty, so the schema now requires at least one alphanumeric character in the canonical form.
Problem
Integration identity was handled two different ways: records were written under the raw provider string (
_integration:GitHubif you connected withprovider=GitHub), while the/connect/oauthwizard read by probing two candidate value names (raw, then normalized) viagetIntegrationValueCandidates. Every other lookup —integration_get/integration_delete, therefreshAccessToken/createAuthenticatedFetchruntime helpers, and OpenAPI binding auth — was silently case-sensitive, sorefreshAccessToken('github')would miss a record saved asGitHub. Follow-up to #716, where this probe was flagged as the remaining legacy-ish naming path.The single way going forward
Integration identity is the canonical provider key: lowercase kebab via the existing
normalizeProviderKey(letters, numbers,.,_,-). One function, applied at every boundary:canonicalIntegrationNameinintegration-shared.ts;buildIntegrationValueNamenow canonicalizes, so every exact lookup (integration_get/integration_delete, connect wizard, OpenAPI bindings, runtime helpers viaintegration_get) tolerates caller casing while touching exactly one stored key.normalizeIntegrationConfigcanonicalizes the storedname, and both zod schemas reject names with no letters/numbers, so writes (integration_save,connect_oauth) can only produce canonical records.parseIntegrationValueNameonly recognizes canonical keys — an integration exists iff its stored key is canonical.getIntegrationValueCandidates) is deleted; the wizard does one deterministic lookup by provider key.docs/guides/oauth.mdnaming section and theintegration_savecapability description state the rule.No data migration needed: a live audit of the production account's stored
_integration:values (17 records: dropbox, github, github-kent, google, google-business, google-youtube-brand, google-youtube-plus, groupme, linkedin, notion, slack, spotify, spotify-family, telegram, tesla, twitch, x) confirmed every name is already canonical. Per the pre-launch convention this is a direct breaking change with no compat aliasing: a hypothetical non-canonical record would simply stop being treated as an integration.Testing
name: 'GitHub'stores and returnsgithubunder_integration:github;buildIntegrationValueNamecasing/spacing normalization (client and server copies); strictparseIntegrationValueName; rejection of letterless names. Handler tests updated for canonicalintegrationNameresponses.npm run validatefully green (format, lint, typecheck, unit tests, Playwright E2E, MCP E2E). One pre-push E2E run had a single og-images failure ("socket hang up" on the web server's first request) that passed on immediate re-run — flake, not related to this change./connect/oauth?provider=CANVA-Mock(uppercased) against the integration stored as_integration:canva-mock; the wizard resolved the stored record, displayed the canonical value name it loaded from, and completed the full OAuth flow — after which local D1 still contains exactly one canonical record and no_integration:CANVA-Mockduplicate. Screenshot and screen recording of this test are attached to the Cursor agent run.System recap — extends existing primitives (medium risk)
Mode: recap · Base:
main@646826e4· Head:00b124bbClassification: extends — the integration naming contract becomes strictly canonical; no new primitives.
Primitives touched
capability-registryapp-uivalues_integration:value keys are always_integration:<canonical-name>System map
Every integration read/write path now derives its value key through one canonicalization function.
Legend: green = composes (wiring only) · amber = extended by this PR · red = new primitive · gray = context (unchanged, included only when an edge crosses it).
Before / after
Invariants
per-user-isolationuntouched — all value reads/writes remain scoped by userId. Verified live data is already canonical, so the strictness introduces no orphaned records.Summary by CodeRabbit