Repository navigation
Scope API keys to the workspace they were created in - #1622
Merged
Merged
Conversation
An MCP key resolved the owner's active workspace (users.organization_id) and role (users.role) instead of the key's own workspace, so a key created in one workspace reached another one its owner had switched to. Take the workspace from the key and the role from the owner's membership there, for MCP keys and management keys. On /mcp/:serverId an API key only reaches servers of its own workspace, and a server-bound key is filed under the server's workspace.
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
Summary
An API key belongs to the workspace it was created in, but two paths took the workspace or the role from the owner's active workspace instead (
users.organization_id/users.role, which follow the workspace last switched to in the dashboard):resolveUserByKey): the shared/mcpendpoint scoped an unpinned key to the owner's active workspace, so a key created in workspace A listed and ran the tools of workspace B. On/mcp/:serverId, the membership fallback let a key of A reach B's servers.JwtOrManagementKeyGuard): the workspace was already the key's, but the role came fromusers.role, i.e. the role in the active workspace rather than in the key's workspace.Only owners who belong to several workspaces are affected. Single-workspace users (almost everyone) get exactly the same answer as before, because both values are equal for them.
Changes
McpApiKeysService.resolveUserByKey:organizationIdis the key's, androleis the owner's membership role in that workspace. The membership is already loaded for the deactivation check, so there is no extra query.McpApiKeysService.generate: a key bound to a server is filed under the server's workspace.JwtOrManagementKeyGuard: the role comes from the membership in the key's workspace./mcp/:serverId: an API key (authMethod: 'mcp_api_key') passes only when the server is in the key's workspace. Sign-ins (JWT/OAuth) keep the membership fallback for multi-workspace users.McpAuthMiddleware/McpAuthGuard: carryorganizationIdonreq.userlike the combined guard does.Behaviour in both modes: identical for Cloud and self-hosted. Self-hosted instances are usually single-workspace and see no change.
Type
Testing
/mcp/:serverIdrefuses a key on a server of the owner's other workspace and serves it in its own.packages/backend:npx tsc --noEmit -p tsconfig.jsonpasses, andnpm run lintreports 0 errors (11 warnings, all in files this PR does not touch).npx jest --maxWorkers=4, against a scratch database migrated withprisma migrate deploy: 1068 suites passed, 10 skipped (live specs), 32,657 tests passed, 0 failed.Checklist