clients/web: scope AI assistant MCP token to the current organization - #14154
Closed
pieterbeulque wants to merge 1 commit into
Closed
pieterbeulque wants to merge 1 commit into
pieterbeulque wants to merge 1 commit into
Conversation
The AI product-creation assistant cached its MCP access token in the polar_mcp_session cookie and reused it unconditionally. The token is minted with sub_type=organization for the org that was open at mint time, so after switching organizations the assistant kept acting on the first org and created products there. Store the organization id alongside the token in the cookie value and only reuse the cached token when it matches the organization the user has open; otherwise mint a fresh org-scoped token (overwriting the cookie). Legacy bare-token cookie values fail parsing and are treated as a cache miss, so they are transparently re-minted. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
1 issue found across 3 files
Confidence score: 2/5
- In
clients/apps/web/src/app/(main)/dashboard/[organization]/(header)/products/new/ai/chat/route.ts, the cache can reuse the prior account’s user-subject token when accounts switch within the same organization, risking requests being attributed to the wrong user—include the authenticated user subject in the cache key or invalidation logic.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="clients/apps/web/src/app/(main)/dashboard/[organization]/(header)/products/new/ai/chat/route.ts">
<violation number="1" location="clients/apps/web/src/app/(main)/dashboard/[organization]/(header)/products/new/ai/chat/route.ts:251">
P1: When another account logs into the same browser while the organization is unchanged, this cache reuses the previous account's user-subject token because it compares only `organizationId`. Include the authenticated user in the cookie value and validation, or clear this cookie on logout, so MCP actions cannot run with the previous user's permissions.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| return requestCookies.get(CONFIG.AUTH_MCP_COOKIE_KEY)!.value | ||
| const cachedSession = requestCookies.get(CONFIG.AUTH_MCP_COOKIE_KEY) | ||
| if (cachedSession) { | ||
| const cachedToken = parseMCPSessionCookie( |
Contributor
There was a problem hiding this comment.
P1: When another account logs into the same browser while the organization is unchanged, this cache reuses the previous account's user-subject token because it compares only organizationId. Include the authenticated user in the cookie value and validation, or clear this cookie on logout, so MCP actions cannot run with the previous user's permissions.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At clients/apps/web/src/app/(main)/dashboard/[organization]/(header)/products/new/ai/chat/route.ts, line 251:
<comment>When another account logs into the same browser while the organization is unchanged, this cache reuses the previous account's user-subject token because it compares only `organizationId`. Include the authenticated user in the cookie value and validation, or clear this cookie on logout, so MCP actions cannot run with the previous user's permissions.</comment>
<file context>
@@ -242,8 +246,15 @@ async function generateOAT(
- return requestCookies.get(CONFIG.AUTH_MCP_COOKIE_KEY)!.value
+ const cachedSession = requestCookies.get(CONFIG.AUTH_MCP_COOKIE_KEY)
+ if (cachedSession) {
+ const cachedToken = parseMCPSessionCookie(
+ cachedSession.value,
+ organizationId,
</file context>
Contributor
Author
|
Will pick it back up later. |
This branch was successfully deployed
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Bug
The AI product-creation assistant creates products under the wrong organization.
generateOAT()inclients/apps/web/src/app/(main)/dashboard/[organization]/(header)/products/new/ai/chat/route.tscached the MCP access token in thepolar_mcp_sessioncookie and returned it unconditionally whenever the cookie existed. The token is minted withsub_type: 'organization', sub: organizationId— i.e. bound to whichever org was open the first time the assistant was used — and the cookie name is a fixed constant, not per-org.Server-side, an organization-scoped token always resolves to its own org (and 422s if a different
organization_idis passed), so after switching organizations in the dashboard, the assistant kept reusing org A's token and deterministically created products in org A.Fix
Minimal, value-scoped cache:
mcpSessionCookie.tswithserializeMCPSessionCookie/parseMCPSessionCookie— the cookie value now stores{ organizationId, token }as JSON.generateOAT()only reuses the cached token when itsorganizationIdmatches the organization the user currently has open; on mismatch (or any unparseable value) it mints a fresh org-scoped token and overwrites the cookie.hasToolAccessheuristic inPOSTnow also checks the cached token's org instead of mere cookie presence, so a stale token from another org no longer counts as tool access.Verification
mcpSessionCookie.test.ts(4 tests: same-org hit, cross-org miss, legacy bare-token miss, malformed/incomplete payload miss) —pnpm vitest runpasses.pnpm typecheckinclients/apps/web— clean.oxlint --type-awareon the touched files andoxfmt --check— clean.🤖 Generated with Claude Code