Skip to content

fix(devframe): authenticate HTTP MCP requests - #327

Open
antfubot wants to merge 2 commits into
mainfrom
fix/authenticate-mcp-http
Open

fix(devframe): authenticate HTTP MCP requests#327
antfubot wants to merge 2 commits into
mainfrom
fix/authenticate-mcp-http

Conversation

@antfubot

@antfubot antfubot commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Why

The route-based MCP endpoint treated a caller-provided Origin as authorization. Origin is DNS-rebinding/cross-site hardening, but a native client can send any value — so a reachable route could invoke privileged agent tools without proving identity. @devframes/next/hub enabled that route by default. Implements plans/002-authenticate-mcp-http.md.

What changed

An independent identity gate now guards the MCP route, checked after the origin gate (the two stay separate defenses):

  • McpRouteOptions.authorization — a non-empty bearer token string (constant-time compared against Authorization: Bearer <token>), a (request) => boolean callback, or explicit false for an origin-only local opt-out.
  • mcp: true is shorthand for the bearer read from DEVFRAME_MCP_AUTH_TOKEN. A missing/empty token, or an object config omitting authorization, fails startup with new diagnostic DF0077 instead of mounting an unauthenticated route.
  • Missing/invalid bearer → 401 + WWW-Authenticate: Bearer; disallowed origin stays 403. Origin runs first; a callback governs identity only and cannot relax the origin gate. Responses are generic — nothing reveals whether a token was close.
  • @devframes/next/hub flips its omitted MCP default from enabled to disabled; callers opt in with an explicit policy.
  • devframe connect reads DEVFRAME_MCP_AUTH_TOKEN and presents it as the bearer (never a CLI flag — args are process-visible). ConnectServerOptions.authToken accepts one token or a (record) => string | undefined resolver for fleets with distinct credentials.

Credentials live only in configuration and the Authorization header — never in URLs, connection metadata, the instance registry, logs, diagnostics, or command-line arguments.

Docs (security, hub-initiate, MCP adapter, Vite/Next framework pages), the DF0077 error page, and the affected API snapshots are updated; runnable examples use an environment-backed policy (or an explicit loopback authorization: false).

Notes for reviewers

  • The DevframeNextHost.mountMcp options parameter is now required (it must carry authorization) — an intentional breaking narrowing captured in the @devframes/next/hub snapshot.
  • Full-workspace build/knip/typecheck can't complete in the sandbox (pre-existing: plugin-git build hits an EACCES, and starter/unbuilt-dist packages fail identically on main). The changed packages (devframe, @devframes/hub, @devframes/next) build and typecheck clean, and all targeted MCP/host/connector test suites pass.

This PR was created with the help of an agent.

The route-based MCP endpoint treated a caller-provided Origin as authorization, so any local process (or a native client spoofing an Origin) could invoke privileged agent tools. Origin is DNS-rebinding hardening, not identity.

Add an independent identity gate to the MCP route, checked after the origin gate:

- McpRouteOptions.authorization: a bearer token string (constant-time compared), a (request) => boolean callback, or false for an origin-only local opt-out.
- mcp: true is shorthand for the bearer read from DEVFRAME_MCP_AUTH_TOKEN; a missing token or an object without authorization fails startup with new diagnostic DF0077 rather than mounting an unauthenticated route.
- Missing/invalid bearer -> 401 + WWW-Authenticate: Bearer; disallowed origin stays 403. A callback governs identity only and cannot relax the origin gate.
- @devframes/next/hub now defaults MCP to disabled; callers opt in with an explicit policy.
- devframe connect reads DEVFRAME_MCP_AUTH_TOKEN and presents it as the bearer; ConnectServerOptions.authToken accepts one token or a per-instance resolver. Credentials live only in configuration and the Authorization header.

Created with the help of an agent.
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated
devframe Ready Ready Preview Sep 1, 2026 7:04am UTC
…nnect flow

The hub-next example now enables its aggregate MCP route with the env-backed mcp: true policy, so the Next server needs the bearer at boot and the connect spec's spawned connector needs the same one. Share one token between playwright.config's hub-next webServer env and the withConnectClient spawn env.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant