Skip to content

fix(mcp): isolate callbacks for OAuth servers without response issuers - #2868

Merged
reachjalil merged 1 commit into
different-ai:devfrom
reachjalil:feature/oauth-isolated-callback-defense
Jul 16, 2026
Merged

fix(mcp): isolate callbacks for OAuth servers without response issuers#2868
reachjalil merged 1 commit into
different-ai:devfrom
reachjalil:feature/oauth-isolated-callback-defense

Conversation

@reachjalil

@reachjalil reachjalil commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • keep the deployment-wide shared callback strict for authorization servers that advertise RFC 9207 response issuers
  • automatically switch an OAuth connection to a connection-specific callback when discovery does not advertise response-issuer support and does advertise PKCE S256
  • treat an unexpected iss as untrusted compatibility data only after the distinct redirect URI is active; it is never used to select an issuer or token endpoint
  • restart dynamic client registration before opening the authorization page so the registered and requested redirect URIs remain exact
  • require pre-registered clients to allowlist and persist the exact connection-specific callback before reconnecting

This is a general protocol-level follow-up to #2853. It does not contain a provider hostname allowlist or a user-facing security toggle.

Problem

Some authorization servers do not advertise RFC 9207 authorization-response issuer support, but still return a malformed or provider-internal iss value. Exact issuer validation correctly stops those callbacks before token exchange, while globally ignoring issuer mismatches would weaken OAuth mix-up protection.

PKCE and signed state are retained, but neither is treated as a replacement for mix-up protection.

Security model

  • RFC 9207 path: shared callback, exact issuer match, fail closed on missing or mismatched iss
  • RFC 9700 fallback: redirect URI unique to the MCP connection, signed state bound to organization/member/connection/issuer/callback mode, and PKCE S256
  • an isolated callback is selected only from discovered authorization-server metadata; providers advertising response-issuer support remain on the strict path
  • malformed or mismatched response issuers are ignored only on the isolated path and are logged without recording their raw value
  • token exchange continues against the issuer and endpoints selected during validated discovery, never against response parameters

Connection lifecycle

  1. Start with the normal shared callback and perform MCP/OAuth discovery.
  2. If response-issuer support is absent and PKCE S256 is available, abandon that pending transaction.
  3. Persist isolated-v1, invalidate incompatible SDK registration/token state, and restart registration using the connection-specific callback.
  4. Complete the callback using the signed state and isolated redirect as the mix-up defense.

For a pre-registered OAuth client, the administrator must add the exact connection-specific callback and re-save the client configuration. Existing legacy callback connections remain unchanged.

Validation

  • pnpm --dir packages/enterprise-mcp-client test — 44 passed
  • pnpm --dir ee/apps/den-api exec bun test test/generic-oauth-state.test.ts test/mcp-connections-connect-start.test.ts — 19 passed, 111 assertions
  • pnpm --dir ee/apps/den-api exec tsc -p tsconfig.json --noEmit --pretty false — passed
  • pnpm --dir ee/apps/den-web exec tsc -p tsconfig.json --noEmit --pretty false — passed
  • git diff --check upstream/dev...HEAD — passed

The Den route test exercises discovery, two registrations, isolated redirect selection, signed state, malformed provider iss, PKCE token exchange, authenticated MCP initialization/tool discovery, and encrypted credential persistence. Strict shared and existing legacy callback coverage also passes.

CI status

The initial GitHub Actions attempt and one failed-job retry both stopped before project code ran because api.github.com returned HTTP 503. Test runners failed while fetching Bun; image builds failed in Docker metadata with GitHub's 503 response. Schema/migration, Helm, and i18n checks pass. No code change was made for this external failure.

Risks and limits

  • selecting the isolated mode invalidates incompatible SDK registration and connected-account credentials for that connection because the OAuth client redirect contract changes
  • providers without dynamic registration require administrator action to allowlist the scoped callback
  • the deterministic end-to-end provider simulation passes; a live Descript consent flow was not run because it requires an external user authorization session
  • no database migration is required; callback mode is stored in the existing OAuth configuration JSON

@reachjalil
reachjalil marked this pull request as ready for review July 16, 2026 23:11
@reachjalil
reachjalil merged commit 00fa6d4 into different-ai:dev Jul 16, 2026
6 of 16 checks passed
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
openwork-landing Ready Ready Preview, Comment, Open in v0 Jul 17, 2026 12:06am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
openwork-diagnostics Skipped Skipped Jul 17, 2026 12:06am

@vercel
vercel Bot temporarily deployed to Preview – openwork-diagnostics July 16, 2026 23:51 Inactive
@vercel

vercel Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

@reachjalil is attempting to deploy a commit to the Different AI Team on Vercel.

A member of the Team first needs to authorize it.

@reachjalil
reachjalil deleted the feature/oauth-isolated-callback-defense branch July 17, 2026 07:06
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