Skip to content

fix(mcp): pass SSRF-guarded fetch into OAuth callback token exchange#5399

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/mcp-oauth-callback-ssrf-guard
Jul 3, 2026
Merged

fix(mcp): pass SSRF-guarded fetch into OAuth callback token exchange#5399
waleedlatif1 merged 1 commit into
stagingfrom
fix/mcp-oauth-callback-ssrf-guard

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator

Summary

  • The OAuth callback route's token exchange (mcpAuth(...)) was calling the MCP SDK's auth() without a fetchFn, so it defaulted to the global fetch for the token-endpoint request.
  • Sibling files apps/sim/lib/mcp/oauth/probe.ts and apps/sim/lib/mcp/oauth/revoke.ts already wire createSsrfGuardedMcpFetch() into their outbound requests — this route was just missing the same guard.
  • Passed fetchFn: createSsrfGuardedMcpFetch() alongside the existing serverUrl/authorizationCode options, matching the established pattern exactly.

Type of Change

  • Bug fix

Testing

  • Added apps/sim/app/api/mcp/oauth/callback/route.test.ts covering that the token exchange call passes the guarded fetch instance through to mcpAuth, alongside the existing serverUrl/authorizationCode args.
  • bun vitest run on the new test file and the sibling start/route.test.ts — both pass.
  • bun run check:api-validation, bun run type-check, and bunx biome check all pass.

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)

Mirrors the same wiring already used by probe.ts and revoke.ts, so the
callback's token-exchange request goes through the same guarded fetch as
the rest of the OAuth flow.
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 3, 2026 10:10pm

Request Review

@cursor

cursor Bot commented Jul 3, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches OAuth token exchange and SSRF protections on server-controlled URLs; change is small and aligned with existing MCP OAuth patterns.

Overview
The MCP OAuth callback route now passes createSsrfGuardedMcpFetch() as fetchFn when calling the SDK’s mcpAuth for the authorization-code token exchange, instead of relying on the default global fetch. That matches how other MCP OAuth paths (e.g. probe and revoke) already guard outbound HTTP.

A new route.test.ts asserts the callback handler invokes createSsrfGuardedMcpFetch once and forwards the returned fetch into mcpAuth with the expected serverUrl and authorizationCode.

Reviewed by Cursor Bugbot for commit e14076b. Configure here.

@greptile-apps

greptile-apps Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a missing SSRF guard in the MCP OAuth callback's token exchange step. The mcpAuth() call in route.ts was using the global fetch by default, unlike the sibling probe.ts and revoke.ts files which both explicitly wire createSsrfGuardedMcpFetch() into their outbound requests.

  • route.ts: Adds fetchFn: createSsrfGuardedMcpFetch() to the mcpAuth(provider, {...}) call, matching the established pattern in probe.ts and revoke.ts and ensuring every outbound fetch during token exchange is validated against the SSRF policy per request.
  • route.test.ts: New focused test file that verifies the guarded fetch instance is created and forwarded to mcpAuth as fetchFn, alongside the existing serverUrl/authorizationCode args.

Confidence Score: 5/5

Safe to merge — a single-line targeted fix that closes an SSRF guard gap in the OAuth token exchange, consistent with the established pattern in all sibling files.

The change is minimal and surgical: one import and one property added inside an already-validated code path. It exactly mirrors probe.ts and revoke.ts, which have been carrying the same guard pattern without issues. The accompanying test directly asserts the new behavior. No logic is restructured and no error paths are altered.

No files require special attention.

Important Files Changed

Filename Overview
apps/sim/app/api/mcp/oauth/callback/route.ts Adds fetchFn: createSsrfGuardedMcpFetch() to the mcpAuth call, closing a gap where the token exchange used unguarded global fetch while all sibling OAuth flows were already SSRF-protected.
apps/sim/app/api/mcp/oauth/callback/route.test.ts New test verifying that createSsrfGuardedMcpFetch() is called and its result forwarded as fetchFn to mcpAuth; directly targets the behavior introduced by the fix.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant CallbackRoute as OAuth Callback Route
    participant SSRF as createSsrfGuardedMcpFetch
    participant mcpAuth as mcpAuth (MCP SDK)
    participant DNS as DNS + IP Validation
    participant TokenEndpoint as Token Endpoint

    Browser->>CallbackRoute: "GET /api/mcp/oauth/callback?state=&code="
    CallbackRoute->>CallbackRoute: validateState, assertSafeOauthServerUrl
    CallbackRoute->>SSRF: createSsrfGuardedMcpFetch()
    SSRF-->>CallbackRoute: guardedFetch (FetchLike)
    CallbackRoute->>mcpAuth: "mcpAuth(provider, { serverUrl, authorizationCode, fetchFn: guardedFetch })"
    mcpAuth->>guardedFetch: fetch(token_endpoint, ...)
    guardedFetch->>DNS: validateMcpServerSsrf(url)
    DNS-->>guardedFetch: resolvedIP (or block)
    guardedFetch->>TokenEndpoint: pinned HTTP request
    TokenEndpoint-->>guardedFetch: token response
    guardedFetch-->>mcpAuth: Response
    mcpAuth-->>CallbackRoute: AUTHORIZED
    CallbackRoute-->>Browser: htmlClose (success)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Browser
    participant CallbackRoute as OAuth Callback Route
    participant SSRF as createSsrfGuardedMcpFetch
    participant mcpAuth as mcpAuth (MCP SDK)
    participant DNS as DNS + IP Validation
    participant TokenEndpoint as Token Endpoint

    Browser->>CallbackRoute: "GET /api/mcp/oauth/callback?state=&code="
    CallbackRoute->>CallbackRoute: validateState, assertSafeOauthServerUrl
    CallbackRoute->>SSRF: createSsrfGuardedMcpFetch()
    SSRF-->>CallbackRoute: guardedFetch (FetchLike)
    CallbackRoute->>mcpAuth: "mcpAuth(provider, { serverUrl, authorizationCode, fetchFn: guardedFetch })"
    mcpAuth->>guardedFetch: fetch(token_endpoint, ...)
    guardedFetch->>DNS: validateMcpServerSsrf(url)
    DNS-->>guardedFetch: resolvedIP (or block)
    guardedFetch->>TokenEndpoint: pinned HTTP request
    TokenEndpoint-->>guardedFetch: token response
    guardedFetch-->>mcpAuth: Response
    mcpAuth-->>CallbackRoute: AUTHORIZED
    CallbackRoute-->>Browser: htmlClose (success)
Loading

Reviews (1): Last reviewed commit: "fix(mcp): pass SSRF-guarded fetch into O..." | Re-trigger Greptile

@waleedlatif1 waleedlatif1 merged commit afe3d32 into staging Jul 3, 2026
18 checks passed
@waleedlatif1 waleedlatif1 deleted the fix/mcp-oauth-callback-ssrf-guard branch July 3, 2026 22:34
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