Skip to content

fix(mcp): deny API key management to team API keys - #78

Merged
rajat1saxena merged 2 commits into
mainfrom
fix/mcp-team-key-api-key-management
Oct 19, 2026
Merged

rajat1saxena merged 2 commits into
mainfrom
fix/mcp-team-key-api-key-management

Conversation

@clbotdev

Copy link
Copy Markdown
Contributor

Fixes the high-severity Security item 1 in #59: a team API key could create and delete team keys over MCP.

Problem

authorizeMcpTool (apps/api/src/mcp/policy.ts) returned null (allowed) for every team_key caller, and the list_api_keys / create_api_key / delete_api_key handlers don't check anything themselves. A leaked key could mint replacement keys for itself, so revoking it wouldn't lock the attacker out, and it could revoke the team's other keys. REST already refuses this: /teams/* returns 403 user_auth_required to key callers.

Fix

As the issue suggests (FrontLit's apiKeyDeniedTools), the central MCP policy now has a teamKeyDeniedTools set: list_api_keys, create_api_key, delete_api_key. A team_key caller asking for one of these gets a user_auth_required tool error before the handler runs, so no key queries execute. The denial is logged through the existing scopeDecision: "denied" path. OAuth callers still need api_keys:read / api_keys:write, as before. All other team-key behaviour is unchanged.

The check lives in the policy layer rather than in each handler, because every tool already goes through it, so the rule sits in one place next to the scope policy.

Tests (apps/api/src/mcp/server.test.ts)

  • Unit test: authorizeMcpTool denies all three tools to a team key with user_auth_required, and allows them for OAuth with the api_keys scopes.
  • End-to-end through the real MCP client and server: team-key tools/call for all three tools returns isError with user_auth_required, and getApiKeysByTeamId / createApiKey / deleteApiKey are never called.
  • Regression: an OAuth session with api_keys:write can still call delete_api_key.
  • I checked that the two new denial tests fail without the policy change.

Docs updated: apps/docs/.../developers/mcp.mdx and apps/api/docs/mcp-2026-07-28-migration.md.

Checks run locally (same steps as CI)

pnpm install --frozen-lockfile, pnpm lint, prettier --check packages apps *.md, the package builds (api-contract, email-editor, email-blocks), pnpm test, and apps/api tsc --noEmit + pnpm typecheck: all pass.

Not in this PR

#59 also lists items 2–8: archived-team keys still authenticating, any team member managing keys over REST, last_used_at, revoked keys being listed, 204 on unknown key delete, the shared org-key display prefix, and stale docs. This PR doesn't fix those, so it doesn't close #59 and leaves the remaining items tracked there.

Refs #59

A team API key could call list_api_keys, create_api_key and
delete_api_key over MCP because authorizeMcpTool returned early for
team keys. A leaked key could therefore mint replacement keys for
itself or revoke the team's other keys. REST already refuses this with
user_auth_required.

Deny these three tools to team_key callers in the central MCP policy,
returning a user_auth_required tool error. OAuth callers with the
api_keys scopes are unaffected.

Addresses item 1 of #59.
…key-management

# Conflicts:
#	apps/api/src/mcp/policy.ts
#	apps/api/src/mcp/server.test.ts
#	apps/docs/content/docs/developers/mcp.mdx
@rajat1saxena
rajat1saxena merged commit 531d1f2 into main Oct 19, 2026
6 checks passed
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.

API key management: MCP key minting, archived-team keys, and listing bugs

2 participants