Skip to content

Refuse sign-ins for another workspace's MCP server (#1609) - #1613

Merged
helpcodeai merged 2 commits into
mainfrom
fix/oauth-wrong-workspace-1609
Nov 9, 2026
Merged

helpcodeai merged 2 commits into
mainfrom
fix/oauth-wrong-workspace-1609

Conversation

@helpcodeai

Copy link
Copy Markdown
Contributor

Summary

A person who signs in with the account of their other workspace, while their AI client points at a server URL (/mcp/<serverId>) of a workspace that account does not belong to, used to get a token that the server refused on every call with a 403. Clients do not sign in again after a 403, so the person could not fix it from the chat. On Cloud one user was refused 510 times in 13 hours.

Now the consent step refuses such a sign-in, and the endpoint answers a stray sign-in token with a 401 that makes the client sign in again.

Changes

  • auth/login.controller.ts: completeSignIn (password, dashboard-session Approve, Google/Microsoft/GitHub, email code) checks the server the client asked for (RFC 8707 resource, the signed mcp_resource cookie). If the server exists and the user is neither in its workspace as primary organization nor an active member, no login_user cookie and no grant: redirect to /auth/login?switch=1 with a message to sign in with an account of that workspace. The server name is not shown. Same membership rule as the endpoint. Unknown server ids and lookup errors keep the existing flow (the endpoint still checks).
  • mcp-server/mcp-endpoint.controller.ts: for a non-member with authMethod: 'jwt' (OAuth access tokens and app JWTs), 401 with WWW-Authenticate: Bearer realm="AnythingMCP MCP Server", error="invalid_token", resource_metadata="<base>/.well-known/oauth-protected-resource/mcp/<serverId>". API keys keep the 403 and its message. The warn log line is unchanged.
  • auth/mcp-www-authenticate.util.ts: the header value, moved out of McpCombinedAuthGuard.deny() so the guard and the endpoint build it the same way (no behaviour change in the guard).
  • auth/resource-indicator.middleware.ts: comment updated: the requested resource may now also refuse an authorization; it can only narrow, never widen.

Not changed: the SSO (identity provider) MCP path in sso.controller.ts, which picks the provider from the requested server's workspace already; the endpoint 401 covers it anyway.

Cloud and self-hosted behave the same. It only matters where an instance has more than one workspace. No Prisma change, nothing under ee/.

Type

  • Bug fix
  • New feature
  • Documentation
  • Refactoring
  • Performance improvement
  • Other (describe below)

Testing

  • Added/updated unit tests
  • Tested manually (describe steps below)
  • No tests needed (explain why)

New auth/login.controller.wrong-workspace.spec.ts: refused for a non-member (no login_user, no grant, redirect with the message); allowed for a member whose primary workspace is another one; allowed for the primary workspace without a membership query; no server lookup when the client named no server; unknown server id left to the flow.
mcp-server/mcp-endpoint.controller.spec.ts: non-member JWT gets 401 with the exact header and the warn line; an API key of another workspace still gets 403 without the header; the listen and "organization unknown" cases now expect 401.

Commands, in packages/backend (after npm ci and npx prisma generate):

  • npx jest --maxWorkers=4 src/auth src/mcp-server src/mcp-servers: 81 suites passed, 1,241 tests passed, 5 skipped. 1 suite failed: src/auth/jwt-or-management-key.guard.spec.ts (13 tests) needs a live Postgres (prisma.organization.create in beforeAll), none was running locally; unrelated to this change, CI runs it with a database.
  • npx tsc --noEmit -p tsconfig.json: exit 0.
  • npx eslint on the 7 touched files: exit 0.

Checklist

  • My code follows the existing code style
  • I have tested my changes locally
  • I have updated documentation (if applicable)
  • All existing tests pass (npm test)
  • This PR has a descriptive title

No user documentation describes this refusal, so none changed.

Related Issues

Fixes #1609

A client that names one MCP server by URL no longer gets a token for an
account outside that server's workspace: the sign-in form comes back with
a message to use an account of that workspace. A sign-in token that still
reaches a server of another workspace gets 401 with invalid_token instead
of 403, so the client signs in again. API keys keep the 403.
@helpcodeai
helpcodeai enabled auto-merge (squash) November 9, 2026 17:06
@helpcodeai
helpcodeai merged commit bc0b55f into main Nov 9, 2026
13 of 14 checks passed
@helpcodeai
helpcodeai deleted the fix/oauth-wrong-workspace-1609 branch November 9, 2026 17:20
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.

MCP OAuth: a sign-in for the wrong workspace is refused forever

1 participant