fix(mcp): enforce --auth-token on the MCP bridge instead of ignoring it - #2844
fix(mcp): enforce --auth-token on the MCP bridge instead of ignoring it#2844kovtcharov-amd wants to merge 2 commits into
Conversation
`gaia mcp start --auth-token <token>` printed "Authentication enabled" and
then dropped the token: it was never passed to `start_server`, and no request
handler looked at the Authorization header. Every endpoint answered 200
identically with no token, the right token, or a wrong one, so anyone who
exposed the bridge past loopback had an open tool-invocation endpoint while
believing it was protected. `docs/reference/cli.mdx` published the guarantee
too ("require Authorization: Bearer <token> on every request"), so the false
assurance was documented, not merely implied. The flag has been inert since
v0.11.0.
Thread the token from the CLI into the bridge and enforce it in the handler:
401 for a missing or malformed Authorization header, 403 for a wrong token,
compared with `secrets.compare_digest` so the check is constant-time. The gate
runs before the request body is read and before any tool dispatches. `/health`
stays public — liveness probes and `gaia mcp status` depend on it, and it
exposes only counts, never agent or tool names.
The token is also no longer passed on the child's command line in `--background`
mode, where `ps` made it readable by any local user; it goes through
`GAIA_MCP_AUTH_TOKEN`, which is now the documented way to supply it. The
`mcp status` / `test` / `agent` client commands gained `--auth-token` so they
can still reach a protected bridge, and report an actionable message on 401/403.
Default behaviour is unchanged: with no token configured the bridge stays open
and the wildcard-bind warning still fires.
|
Verdict: Approve This PR makes No blocking issues. The auth gate is placed before request-body reads and tool dispatch, the token comparison is constant-time, and the edge cases (malformed/missing header, wrong scheme, non-ASCII, prefix truncation) are all covered by real over-the-socket tests. Docs and CLI help are consistent with the code. One thing worth being aware of (not a blocker, and it's documented + tested): Real-world evidence
CLI auth banner + protected HTTP contract (direct requests to the running bridge): The 🔍 Technical detailsStrengths
🟢 Minor (non-blocking, no change required)
|
gaia mcp start --auth-token <token>announced "🔒 Authentication enabled" and then ignored the token entirely — it was never handed to the HTTP server, and no handler read theAuthorizationheader. Every endpoint returned an identical 200 with no token, the correct token, or a wrong one, so anyone who exposed the bridge past loopback (--host 0.0.0.0, Docker, a reverse proxy, an SSH tunnel) had an open tool-invocation endpoint while believing it was locked down.docs/reference/cli.mdxpromised the same thing the code didn't do. Now the token is enforced:401for a missing or malformed header,403for a wrong one, constant-time compare, checked before the body is read or any tool runs./healthstays public for liveness probes and reports only counts.Two smaller holes closed along the way: in
--backgroundmode the token was passed on the child's command line, wherepsexposed it to any local user — it now travels viaGAIA_MCP_AUTH_TOKEN; andgaia mcp status/test/agentgained--auth-tokenso they can still reach a protected bridge. Default behaviour is unchanged — with no token the bridge stays open and the wildcard-bind warning still fires.Test plan
pytest tests/unit/test_mcp_bridge_auth.py tests/unit/test_mcp_bridge_bind.py— 43 passPOST /(tools/list,tools/call),/chat,/llm— 401/403 and the tool must not executegaia mcp statuswith no token prints the "requires authentication" hint; with--auth-token secret123orGAIA_MCP_AUTH_TOKEN=secret123it prints the full inventorygaia mcp start --background --auth-token <t>— confirm the token is absent from the child's command line (ps -ef/Get-CimInstance Win32_Process) and that enforcement is still active--auth-token— every endpoint still returns 200 and the0.0.0.0warning still fires