Summary
The database schema has an API-key scopes column, but API-key creation does not populate it, public mapping strips it, REST auth does not carry it, and MCP grants all API keys every MCP scope.
Evidence
- packages/db/src/schema/api-keys.ts:44-51 defines an optional JSON-encoded scopes column.
- apps/web/src/lib/server/domains/api-keys/api-key.service.ts:23-35 maps API-key rows to public shape and strips scopes.
- apps/web/src/lib/server/domains/api-keys/api-key.service.ts:70-120 creates API keys without accepting or storing scopes.
- apps/web/src/lib/server/domains/api-keys/api-key.service.ts:133-162 supports an optional scope check internally, but the main auth path does not use it.
- apps/web/src/lib/server/domains/api/auth.ts:50-78 returns API auth context without scopes.
- apps/web/src/lib/server/mcp/handler.ts:36-44 defines all MCP scopes.
- apps/web/src/lib/server/mcp/handler.ts:153-173 returns scopes: ALL_SCOPES for API-key callers.
- apps/web/src/lib/server/mcp/tools.ts:166-173 enforces scopes, but API keys have already been granted all scopes.
Impact
Any valid team API key effectively becomes a full-scope MCP credential. This prevents least-privilege service accounts and makes key leakage more damaging.
Recommended fix
Wire scopes through API-key creation, listing, update, verification, REST auth context, and MCP auth context. Require each REST route and MCP tool to declare and enforce the minimum required scope. Existing keys should be migrated as explicit legacy full-access keys until rotated.
Acceptance criteria
- Admins can create API keys with selected scopes.
- API auth context includes the verified key’s scopes.
- MCP API-key auth uses stored scopes, not ALL_SCOPES.
- REST endpoints enforce route-specific scopes in addition to role checks.
- Existing full-access keys are visibly labeled as legacy full-access keys.
- Tests cover read-only keys, write-denied keys, expired keys, revoked keys, and MCP tool scope failures.
Summary
The database schema has an API-key scopes column, but API-key creation does not populate it, public mapping strips it, REST auth does not carry it, and MCP grants all API keys every MCP scope.
Evidence
Impact
Any valid team API key effectively becomes a full-scope MCP credential. This prevents least-privilege service accounts and makes key leakage more damaging.
Recommended fix
Wire scopes through API-key creation, listing, update, verification, REST auth context, and MCP auth context. Require each REST route and MCP tool to declare and enforce the minimum required scope. Existing keys should be migrated as explicit legacy full-access keys until rotated.
Acceptance criteria