Skip to content

feat(mcp): OAuth 2.1 + PKCE for outbound MCP servers#4441

Open
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/mcp-oauth
Open

feat(mcp): OAuth 2.1 + PKCE for outbound MCP servers#4441
waleedlatif1 wants to merge 1 commit intostagingfrom
waleedlatif1/mcp-oauth

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

Summary

  • Adds spec-compliant OAuth 2.1 + PKCE support for outbound MCP servers via the SDK's OAuthClientProvider
  • Auto-detects OAuth requirement on server create via unauthenticated probe (WWW-Authenticate / oauth-protected-resource)
  • Persists per-user-per-server tokens (encrypted) in new mcp_server_oauth table; SDK refreshes automatically before expiry
  • Popup-based consent flow (/api/mcp/oauth/start/api/mcp/oauth/callback) with state CSRF protection
  • Pre-registered OAuth client support (Client ID + Secret in Advanced settings) for servers without RFC 7591 DCR
  • Surfaces reauth_required from tool execution when refresh token is invalid so the UI can prompt to reconnect

Type of Change

  • New feature

Testing

Tested manually against OAuth-protected MCP servers (Linear). Existing header-auth servers regression-checked.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

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

Project Deployment Actions Updated (UTC)
docs Error Error May 10, 2026 7:51pm

Request Review

@cursor
Copy link
Copy Markdown

cursor Bot commented May 5, 2026

PR Summary

High Risk
Adds an OAuth consent flow and encrypted token storage for outbound MCP connections, and changes how tools are executed/discovered across server auth modes; mistakes here can break integrations or expose auth edge cases. Also introduces new DB tables/columns and per-user connection scoping, increasing rollout risk.

Overview
Enables outbound MCP servers to authenticate via OAuth 2.1 + PKCE (in addition to header/none auth), including auto-probing server auth requirements and a popup-based consent flow.

Adds new /api/mcp/oauth/start and /api/mcp/oauth/callback routes plus an SDK OAuthClientProvider implementation that persists per-user OAuth state (client info, tokens, PKCE verifier/state) in a new encrypted mcp_server_oauth table; callback burns state and refreshes discovered tools.

Extends MCP server CRUD to store authType and optional pre-registered oauthClientId/oauthClientSecret (encrypted), hides secrets via hasOauthClientSecret, and clears per-user OAuth rows when URL/credentials change. Updates tool execution/discovery and the persistent connection manager to attach OAuth providers, scope managed connections by (serverId,userId), and return a 401 reauth_required when OAuth is missing/expired so the UI can prompt reconnect.

Reviewed by Cursor Bugbot for commit aa8078d. Configure here.

Comment thread apps/sim/lib/mcp/oauth/provider.ts
Comment thread apps/sim/hooks/queries/mcp.ts
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 5, 2026

Greptile Summary

This PR implements full OAuth 2.1 + PKCE support for outbound MCP servers, introducing a mcp_server_oauth DB table for per-user encrypted token storage, a SimMcpOauthProvider that drives the SDK's standard auth flow, and a popup-based consent UI (/api/mcp/oauth/start/api/mcp/oauth/callback). Several security issues found in earlier review passes have all been addressed in subsequent commits.

  • New OAuth infrastructure: mcp_server_oauth table, SimMcpOauthProvider, encrypted storage helpers, and probe-based authType auto-detection are wired together coherently; connection-manager cache keys are correctly scoped per-user for OAuth servers.
  • reauth_required propagation: Tool execution surfaces a structured { code: 'reauth_required', serverId } response for all OAuth error types, and both discoverServerTools and getServerSummaries mark expired-token servers as disconnected rather than error.
  • Remaining minor concerns: The state-lifetime TTL in loadOauthRowByState is driven by updatedAt which any row write can bump, and the form modal's connection-test bypass heuristic is broader than necessary.

Confidence Score: 5/5

Safe to merge; OAuth plumbing is well-structured, all previously identified security gaps are closed, and remaining findings are minor hardening suggestions that do not affect correctness or security of the happy path.

All significant issues from prior review rounds are confirmed addressed. The two new findings are narrow: state TTL via updatedAt is low-risk because states are single-use, and the form modal auth-bypass heuristic affects only the UX guard, not the server-side security boundary.

apps/sim/lib/mcp/oauth/storage.ts (state TTL via updatedAt) and the mcp-server-form-modal.tsx (connection-test bypass heuristic) would benefit from a follow-up hardening pass.

Important Files Changed

Filename Overview
apps/sim/lib/mcp/oauth/storage.ts New file for DB persistence of MCP OAuth state; tokens and client info are encrypted correctly, PKCE verifier is encrypted, state stored as SHA-256 hash. Minor: loadOauthRowByState TTL driven by updatedAt which unrelated row writes can bump.
apps/sim/lib/mcp/oauth/provider.ts New SimMcpOauthProvider correctly implements OAuthClientProvider, delegates to encrypted storage helpers, supports both pre-registered and DCR clients.
apps/sim/app/api/mcp/oauth/callback/route.ts Burns state before token exchange, validates session user matches flow initiator, HTML-escapes all reflected values, uses postMessage with same-origin check.
apps/sim/app/api/mcp/oauth/start/route.ts Correctly uses withMcpAuth middleware, validates server authType, surfaces authorization URL via McpOauthRedirectRequired signal.
apps/sim/app/api/mcp/servers/route.ts POST handler probes new/URL-changed servers and preserves existing authType on same-URL edits; OAuth token cleanup correctly added to upsert transaction.
apps/sim/app/api/mcp/servers/[id]/route.ts PATCH handler wrapped in transaction, clears mcp_server_oauth when URL or OAuth credentials change, strips raw secret from response.
apps/sim/app/api/mcp/tools/execute/route.ts Correctly catches all OAuth error types returning structured reauth_required response with correct serverId; timeout handle properly cleared.
apps/sim/lib/mcp/service.ts Threads userId into createClient for OAuth servers; discoverServerTools and getServerSummaries handle UnauthorizedError and McpOauthAuthorizationRequiredError by marking servers disconnected.
apps/sim/lib/mcp/connection-manager.ts Cache keys correctly scoped per-user for OAuth servers; disconnectServer removes all per-user connections for a given server ID.
apps/sim/hooks/mcp/use-mcp-oauth-popup.ts Per-server connecting state tracked in a Set; setInterval polling cleaned up on unmount; postMessage origin check enforced.
apps/sim/hooks/queries/mcp.ts Query key structure refactored for hierarchical invalidation; useStartMcpOauth validates https scheme with localhost loopback exception before opening popup.
packages/db/schema.ts New mcp_server_oauth table with correct FK cascades, unique index on (mcpServerId, userId), and state lookup index.
apps/sim/app/workspace/[workspaceId]/settings/components/mcp/components/mcp-server-form-modal/mcp-server-form-modal.tsx OAuth fields added with secretTouched tracking; connection test bypass heuristic is overly broad and could allow saving misconfigured header-auth servers.

Sequence Diagram

sequenceDiagram
    participant UI as Browser/UI
    participant Start as /api/mcp/oauth/start
    participant CB as /api/mcp/oauth/callback
    participant DB as mcp_server_oauth
    participant AS as Auth Server MCP

    UI->>+Start: GET with serverId and workspaceId
    Start->>DB: getOrCreateOauthRow
    DB-->>Start: oauth row
    Start->>AS: SDK mcpAuth metadata discovery plus DCR
    AS-->>Start: McpOauthRedirectRequired
    Start-->>-UI: status redirect with authorizationUrl

    UI->>UI: window.open authorizationUrl
    AS-->>UI: redirect to callback with auth code and state

    UI->>+CB: GET callback
    CB->>DB: loadOauthRowByState hash lookup with TTL
    DB-->>CB: oauth row
    CB->>DB: clearState burn before exchange
    CB->>AS: SDK mcpAuth token exchange
    AS-->>CB: access and refresh tokens
    CB->>DB: saveTokens encrypted
    CB->>DB: clearVerifier
    CB-->>-UI: postMessage ok true
Loading

Reviews (24): Last reviewed commit: "fix(mcp): final audit fixes — state TTL,..." | Re-trigger Greptile

Comment thread apps/sim/app/workspace/[workspaceId]/settings/components/mcp/mcp.tsx Outdated
Comment thread apps/sim/lib/mcp/oauth/storage.ts
Comment thread apps/sim/lib/mcp/oauth/storage.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

Greptile summary findings addressed in f587e82:

  • Edit modal drops existing OAuth Client ID: editInitialData now includes oauthClientId; the API already returns it (only the secret is masked) so the field populates and Advanced auto-expands.
  • Shared OAuth mutation disables all buttons: per-server pending tracked in a local Set<string>; the spinner is scoped to the card whose flow is in progress.
  • Plaintext PKCE codeVerifier: now encrypted at rest via encryptSecret to match tokens/clientInformation.

The point about clearing a pre-registered Client ID by emptying the field is a follow-up — oauthClientId || undefined collapses an intentional clear into a no-op. Will tackle when adding TTL cleanup for abandoned OAuth sessions.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/mcp/servers/[id]/route.ts
Comment thread apps/sim/app/api/mcp/servers/[id]/route.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/mcp/servers/[id]/route.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/mcp/servers/route.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/mcp/tools/execute/route.ts
Comment thread apps/sim/lib/mcp/service.ts Outdated
Comment thread apps/sim/lib/mcp/service.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

Comment thread apps/sim/app/api/mcp/oauth/callback/route.ts
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3f840d6. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/app/api/mcp/servers/[id]/route.ts Outdated
Comment thread apps/sim/app/api/mcp/oauth/callback/route.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Comment thread apps/sim/lib/mcp/oauth/storage.ts
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

Comment thread apps/sim/hooks/mcp/use-mcp-oauth-popup.ts Outdated
Comment thread apps/sim/app/api/mcp/servers/route.ts Outdated
@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit aa8078d. Configure here.

Adds full MCP OAuth 2.1 + PKCE + Dynamic Client Registration (RFC 7591)
support for outbound MCP servers via the SDK's `authProvider` interface.

- `mcp_server_oauth` table holds per-server SDK OAuth artifacts
  (client info, encrypted tokens, PKCE verifier, state) workspace-scoped
  and shared across workspace members.
- `mcp_servers.{auth_type, oauth_client_id, oauth_client_secret}` columns
  capture probe result and optional pre-registered credentials for ASes
  that don't support DCR.
- `SimMcpOauthProvider` implements the SDK's `OAuthClientProvider` with a
  storage-backed redirect-sentinel pattern; the popup flow runs through
  `/api/mcp/oauth/{start,callback}` and posts back to the opener.
- Unauthorized errors during tool execution surface as `reauth_required`
  so the UI can re-prompt without a stale-server flicker.
- Tests, audit script baseline, and turbo bump included.
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