Skip to content

Add subject and claims to AccessToken#2686

Open
maxisbey wants to merge 3 commits into
mainfrom
auth-access-token-subject
Open

Add subject and claims to AccessToken#2686
maxisbey wants to merge 3 commits into
mainfrom
auth-access-token-subject

Conversation

@maxisbey
Copy link
Copy Markdown
Contributor

Summary

Adds optional subject and claims fields to AccessToken so token verifiers can surface the resource owner (sub) and any additional claims to request handlers, and adds subject to AuthorizationCode and RefreshToken so the value can be carried through code-for-token exchange and refresh.

Context.subject exposes the value to tool and resource handlers.

The simple-auth example is updated to thread the subject from login through the introspection response and back into the verifier.

Closes #1038. Supersedes #2209 and #1517 — this combines #2209's subject + claims fields and Context.subject accessor with #1517's threading through the authorization-code flow and example verifier, and adds RefreshToken.subject which both were missing. Thanks to @thomasst, @yukuanj, and @shivama205 for the prior work.

Motivation

Request handlers often need to make decisions based on which end-user a token was issued for, not just which OAuth client. AccessToken previously carried only client_id. The other SDKs already expose this — Go's TokenInfo.UserID, C#'s ClaimsPrincipal — and the claims dict gives an extension point for iss, act, and deployment-specific values without further model changes.

Test plan

  • Model tests for all three types covering defaults and populated values
  • Context.subject tests covering authenticated / unauthenticated / no-subject
  • Full suite, ruff, pyright clean

Breaking changes

None. All new fields are optional with None defaults.

AI Disclaimer

Adds optional `subject` and `claims` fields to `AccessToken` so token
verifiers can surface the resource owner (`sub`) and any additional
claims to request handlers. `subject` is also added to
`AuthorizationCode` and `RefreshToken` so the value can be carried
through code-for-token exchange and token refresh.

`Context.subject` exposes the value to tool and resource handlers.

The simple-auth example is updated to thread the subject from login
through introspection.

Closes #1038.

Reported-by: Thomas Steinacher <@thomasst>
Reported-by: Yukuan Jia <@yukuanj>
Reported-by: Shivam Aggarwal <@shivama205>
@maxisbey maxisbey requested a review from pcarleton May 26, 2026 09:54
- Reword AccessToken.subject's client_credentials note to match RFC 9068
  §2.2 (sub may identify the client itself rather than being unset).
- Add Context.claims so handlers can read iss/act/etc without importing
  get_access_token and handling the raw token model.
- Document that Context.client_id reads MCP request _meta, not the OAuth
  client_id, so it isn't mistaken for the pair to Context.subject.
Comment thread tests/server/mcpserver/auth/test_context_subject.py Outdated
Comment thread tests/server/auth/test_provider.py
Comment thread src/mcp/server/mcpserver/context.py Outdated
Comment thread src/mcp/server/mcpserver/context.py Outdated
Comment thread src/mcp/server/mcpserver/context.py Outdated
Comment thread src/mcp/server/auth/provider.py Outdated
- Drop Context.subject/Context.claims; handlers use get_access_token()
  directly for now. A designed Context auth surface can follow separately.
- Collapse AccessToken.subject/claims docstrings to one-line comments to
  match surrounding fields.
- Move the client_id TODO to a # comment and tighten its docstring note.
- Replace the trivial model and contextvar unit tests with subject
  propagation through MockOAuthProvider in test_auth_integration.py, so
  test_authorization_get now asserts subject survives the full
  /authorize -> /token -> refresh flow over HTTP.
@maxisbey maxisbey marked this pull request as ready for review May 26, 2026 10:50
@maxisbey
Copy link
Copy Markdown
Contributor Author

waiting on @pcarleton review before merging :)

Copy link
Copy Markdown
Member

@pcarleton pcarleton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few non-blocking suggestions, mostly on the example since it's what people copy:

  • examples/.../auth_server.py (introspection response): also return iss, e.g. "iss": str(server_settings.server_url),sub is only unique per issuer, and the example currently never exposes the issuer anywhere in the data path, so a resource server can't key identity on (iss, sub) even if it wants to.
  • examples/.../simple_auth_provider.py: consider a stable id rather than the login username as subject (usernames get renamed/reused) — e.g. the user_id this method already generates and discards a few lines later.
  • src/mcp/server/auth/provider.py: nit — worth squeezing "only unique within the issuing AS; may be absent" into the subject comment, and dropping "verified" from the claims comment (the SDK can't promise that; it's whatever the verifier returned). Might also consider calling this "extra" since it's the full introspection response, not just claims.

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 server: AccessToken class should have field for subject claim ("sub")

3 participants