MCP Session IDs Are Not User Identity: Isolate State by Authenticated Principal
Legacy MCP sessions can correlate transport state but must not authenticate a user. Current MCP removes protocol sessions; both generations require per-request authorization and principal-bound application state.
Read in JapaneseAn MCP session identifier can correlate requests and streams, but it must not decide who the caller is. In the final 2025-11-25 Streamable HTTP transport, a server could issue MCP-Session-Id at initialization and clients had to return it on later requests. The official security guidance still required authorization on every inbound request, prohibited using the session as authentication, and recommended binding session state to information derived from the authenticated user.
That separation prevents two failures: a stolen or guessed session ID impersonating a user, and shared infrastructure delivering one user's queued event or state to another. A random ID helps against guessing; it does not establish ownership.
Identify which protocol generation is deployed
The official versioning page labels 2026-07-28 as Current. That revision removed protocol-level sessions and the Mcp-Session-Id header; each Streamable HTTP request is self-contained and carries version and capability metadata. /draft remains in-progress and is not ready for consumption. Older 2025-11-25 and earlier implementations remain relevant through backward compatibility, so an assessment must record the negotiated version rather than infer behavior from an SDK name.
| Deployment behavior | Required isolation evidence |
|---|---|
Final 2025-11-25 session header | Authenticate every request; store state under authenticated principal plus secure session ID; reject cross-principal reuse |
Current 2026-07-28 request model | Authenticate and authorize every request; derive visible tools and data from that request's principal and scopes |
| Explicit application handle | Treat possession as a reference, not permission; authorize (handle, principal) on every use |
| Shared queue or cache | Include principal/tenant in keys and authorization checks; prevent delivery across users or tenants |
For a legacy session implementation, generate IDs with a cryptographically secure random source, expire and rotate them as appropriate, and keep them out of URLs and routine logs. On every request, validate the bearer token or other credential independently, derive the internal user and tenant from that credential, and verify that the session belongs to that identity. Never accept a client-supplied user ID as the binding evidence.
Current MCP replaces implicit session-scoped application state with explicit server-minted handles as a design pattern. A basket_id, browser context ID, or workflow ID may survive connections and appear in chat history or subagent prompts. It therefore needs the same rule: lookup by handle, then authorize the authenticated principal for that object. Opaque identifiers and sufficient entropy reduce guessing but do not replace access control.
Test the negative cases
Create two test users in different tenants. Have user A create state, then attempt to use its legacy session ID or explicit handle with user B's valid credential. The request must fail without revealing whether the object exists. Repeat across separate server nodes, reconnects, queue delivery, and cached tools/list responses. Verify that logout, token revocation, and scope reduction take effect even if a session or handle remains known.
Also test concurrent requests for the same principal. Isolation must not corrupt legitimate shared state, and authorization must not depend on which load-balancer node receives the request. Record results against the actual negotiated protocol version, authentication middleware, state store, and deployment topology.
Scope and limits
This review addresses identity separation around transport sessions and application handles. It does not define whether two agents acting for the same user should share a cart, browser, or workflow; that is an application policy. It also does not replace token audience validation, scope checks, tenant-aware database authorization, or protection against an attacker who has stolen the user's valid access token.
