fix(devframe): authenticate HTTP MCP requests - #327
Open
antfubot wants to merge 2 commits into
Open
Conversation
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.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The route-based MCP endpoint treated a caller-provided
Originas authorization.Originis 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/hubenabled that route by default. Implementsplans/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 againstAuthorization: Bearer <token>), a(request) => booleancallback, or explicitfalsefor an origin-only local opt-out.mcp: trueis shorthand for the bearer read fromDEVFRAME_MCP_AUTH_TOKEN. A missing/empty token, or an object config omittingauthorization, fails startup with new diagnosticDF0077instead of mounting an unauthenticated route.401+WWW-Authenticate: Bearer; disallowed origin stays403. 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/hubflips its omitted MCP default from enabled to disabled; callers opt in with an explicit policy.devframe connectreadsDEVFRAME_MCP_AUTH_TOKENand presents it as the bearer (never a CLI flag — args are process-visible).ConnectServerOptions.authTokenaccepts one token or a(record) => string | undefinedresolver for fleets with distinct credentials.Credentials live only in configuration and the
Authorizationheader — 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
DF0077error page, and the affected API snapshots are updated; runnable examples use an environment-backed policy (or an explicit loopbackauthorization: false).Notes for reviewers
DevframeNextHost.mountMcpoptions parameter is now required (it must carryauthorization) — an intentional breaking narrowing captured in the@devframes/next/hubsnapshot.build/knip/typecheckcan't complete in the sandbox (pre-existing:plugin-gitbuild hits anEACCES, andstarter/unbuilt-distpackages fail identically onmain). 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.