Skip to content

Bind transport sessions to the authenticated principal#2718

Merged
maxisbey merged 1 commit into
mainfrom
session-credential-binding
May 29, 2026
Merged

Bind transport sessions to the authenticated principal#2718
maxisbey merged 1 commit into
mainfrom
session-credential-binding

Conversation

@maxisbey
Copy link
Copy Markdown
Contributor

Summary

The Streamable HTTP and SSE transports now record the principal that created each session — the OAuth client together with the issuer and subject when the token verifier supplies them — and serve subsequent requests for that session only when they present the same principal. Requests presenting a different principal receive the same 404 response as for an unknown session ID. SSE session entries are also removed when the connection ends rather than retained for the lifetime of the transport.

The new in-process SSE tests bring connect_sse, handle_post_message, and TransportSecurityMiddleware under tracked coverage, so the corresponding no cover pragmas are removed.

Motivation

Brings the Python transports in line with the Go SDK (TokenInfo.UserID-keyed) and C# SDK (UserIdClaim-keyed). Relevant for hosted MCP hosts and gateways where many end-users go through one OAuth client.

Behaviour

Server Effect
No auth, or non-BearerAuthBackend auth None — no principal is recorded and the comparison always passes
BearerAuthBackend, verifier populates subject/claims["iss"] Sessions keyed on {client_id, issuer, subject}
BearerAuthBackend, verifier does not Absent components are None and the comparison degrades to the remaining ones — see the simple-auth example verifier for populating them

Test plan

  • Streamable HTTP: parametrised over POST/GET/DELETE; same-client / different-client / different-subject / different-issuer / unauthenticated
  • SSE: same matrix; verifies session entry is gone after disconnect; in-process round-trip and edge-case tests
  • TransportSecurityMiddleware.validate_request host/origin/content-type tests
  • Full suite, ruff, pyright, ./scripts/test clean

Breaking changes

None for default deployments. A hand-rolled SSE setup that applies BearerAuthBackend only to the POST route and not the GET route would now reject every message; MCPServer applies auth at the app level and is unaffected.

AI Disclaimer

@maxisbey maxisbey force-pushed the session-credential-binding branch 3 times, most recently from 3708dba to 6421a41 Compare May 29, 2026 16:09
Both HTTP transports now record the principal that created each session
— the OAuth client together with the issuer and subject when the token
verifier supplies them — and serve subsequent requests for that session
only when they present the same principal. Requests presenting a
different principal receive the same 404 response as for an unknown
session ID, and SSE session entries are removed when the connection
ends.

Servers without authentication, and authentication backends other than
the built-in BearerAuthBackend, are unaffected: no principal is recorded
and the comparison always passes.

The new in-process SSE tests bring connect_sse, handle_post_message, and
TransportSecurityMiddleware under tracked coverage, so the corresponding
no-cover pragmas are removed.
@maxisbey maxisbey force-pushed the session-credential-binding branch from 6421a41 to 41ab75d Compare May 29, 2026 16:26
@maxisbey maxisbey merged commit 616476f into main May 29, 2026
31 checks passed
@maxisbey maxisbey deleted the session-credential-binding branch May 29, 2026 16:48
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.

2 participants