Fix get_access_token() returning stale token after OAuth refresh#2505
Fix get_access_token() returning stale token after OAuth refresh#2505
Conversation
WalkthroughThe Pre-merge checks and finishing touches❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
@coderabbitai no, this doesn't actually "reverse" the order - its new functionality. The original code only called _sdk_get_access_token() (which reads auth_context_var). It never checked request.scope["user"] at all. |
There was a problem hiding this comment.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/fastmcp/server/dependencies.py(2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
src/**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
src/**/*.py: Python source code must use Python ≥3.10 with full type annotations
Never use bare except - be specific with exception types
Follow Ruff linting and Prettier formatting standards - run uv run prek run --all-files before committing
Prioritize readable, understandable code - clarity over cleverness, avoid obfuscated or confusing patterns
Files:
src/fastmcp/server/dependencies.py
🧬 Code graph analysis (1)
src/fastmcp/server/dependencies.py (2)
src/fastmcp/server/context.py (1)
get_http_request(683-696)src/fastmcp/server/auth/auth.py (1)
AccessToken(36-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Run tests: Python 3.10 on windows-latest
🔇 Additional comments (2)
src/fastmcp/server/dependencies.py (2)
115-139: Well-designed solution to the stale token issue.The implementation correctly addresses issue #1863 by prioritizing
request.scope["user"]over the SDK'sauth_context_var. This ensures that refreshed tokens are immediately available in long-lived HTTP sessions, as the request scope is updated when authentication middleware processes the refresh.The fallback to
_sdk_get_access_token()maintains backward compatibility for non-HTTP contexts (e.g., WebSocket or SDK-only usage).
144-146: Clear comments explain the conversion logic.The expanded comments helpfully document why the conversion is necessary (SDK's
AccessTokenmay lack fields likeclaimsthat FastMCP's version includes).
| from mcp.server.auth.middleware.auth_context import ( | ||
| get_access_token as _sdk_get_access_token, | ||
| ) | ||
| from mcp.server.auth.middleware.bearer_auth import AuthenticatedUser |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
MCP SDK AuthenticatedUser access_token attribute mcp.server.auth.middleware.bearer_auth
💡 Result:
Short answer: the bearer-auth context object uses the field name "token" (or "token" / "access_token" depending on provider implementation) — the MCP Auth SDK's AuthInfo exposes the validated token as token, not a property named access_token. See MCP Auth docs showing auth_info / AuthInfo usage and examples, and an implementation note showing req.auth.token passed by middleware. [1][2]
Sources
- MCP Auth docs — Configure Bearer auth / Retrieve auth info. [1]
- Implementation note / example showing req.auth = { token, clientId, scopes } for middleware. [2]
Incorrect attribute name: use .token instead of .access_token
The MCP SDK's AuthenticatedUser exposes the validated token field as token, not access_token. Line 132 will raise AttributeError at runtime. Change request.scope["user"].access_token to request.scope["user"].token.
🤖 Prompt for AI Agents
In src/fastmcp/server/dependencies.py around lines 9 and ~132, the code is
accessing request.scope["user"].access_token but the MCP SDK's AuthenticatedUser
exposes the validated token as .token; change all uses of .access_token to
.token so the code reads request.scope["user"].token (and update any related
variable names/typing/comments if present) to avoid AttributeError at runtime.
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for the AuthenticatedUser class definition in installed packages or vendored code
rg -n "class AuthenticatedUser" --type=py -A 20
# Search for any usage of AuthenticatedUser in the codebase
rg -n "AuthenticatedUser" --type=py -C 3
# Look for any .token or .access_token attribute usage with AuthenticatedUser
rg -n "\.access_token|\.token" --type=py -C 2 | head -100
# Try to find the MCP SDK package files if they exist in the repository
fd -t f "bearer_auth.py" --exec cat {}
Length of output: 7653
🌐 Web query:
MCP SDK AuthenticatedUser class access_token attribute Python
💡 Result:
The MCP SDK's AuthenticatedUser stores the parsed AccessToken object on the attribute access_token. So in Python you can read the token (or other fields) like:
- If you have the user object: user.access_token (AccessToken instance) and the raw token string at user.access_token.token.
- Using the provided helper: get_access_token() returns the AccessToken from the current request context.
Example:
- request_user = scope["user"]
- raw_token = request_user.access_token.token
- or from context: access = get_access_token(); raw_token = access.token
References: MCP SDK source showing AuthenticatedUser.access_token and auth_context.get_access_token(). [1][2]
Sources
[1] mcp/server/auth/middleware/bearer_auth.py (AuthenticatedUser.access_token).
[2] mcp/server/auth/middleware/auth_context.py (get_access_token() helper).
@jlowin, you're absolutely right, and I apologize for the incorrect analysis!
The web search confirms that the MCP SDK's AuthenticatedUser class does have an access_token attribute that returns an AccessToken object. The code on line 132 using user.access_token is correct as written.
The confusion arose from an incorrect interpretation of the MCP SDK's structure. Your implementation correctly accesses request.scope["user"].access_token to retrieve the AccessToken object from the authenticated user.
🐰💡
🧬 Code Graph Analysis Results
<src/fastmcp/server/auth/auth.py, lines 36-39>
class AccessToken(_SDKAccessToken):
"""AccessToken that includes all JWT claims."""
claims: dict[str, Any] = Field(default_factory=dict)
<src/fastmcp/server/context.py, lines 309-315>
def client_id(self) -> str | None:
"""Get the client ID if available."""
return (
getattr(self.request_context.meta, "client_id", None)
if self.request_context and self.request_context.meta
else None
)
<src/fastmcp/server/context.py, lines 683-696>
def get_http_request(self) -> Request:
"""Get the active starlette request."""
# Deprecated in 2.2.11
if settings.deprecation_warnings:
warnings.warn(
"Context.get_http_request() is deprecated and will be removed in a future version. "
"Use get_http_request() from fastmcp.server.dependencies instead. "
"See https://gofastmcp.com/servers/context#http-requests for more details.",
DeprecationWarning,
stacklevel=2,
)
return fastmcp.server.dependencies.get_http_request()
✏️ Learnings added
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 2505
File: src/fastmcp/server/dependencies.py:9-9
Timestamp: 2025-12-01T15:53:29.674Z
Learning: In the MCP SDK (mcp.server.auth.middleware.bearer_auth), the AuthenticatedUser class has an `access_token` attribute (not `token`) that returns an AccessToken object. To get the raw token string, you would use `user.access_token.token`.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
🧠 Learnings used
Learnt from: jlowin
Repo: jlowin/fastmcp PR: 0
File: :0-0
Timestamp: 2025-12-01T15:48:05.083Z
Learning: PR #2505 in fastmcp adds NEW functionality to get_access_token(): it now first checks request.scope["user"] for the token (which never existed before), then falls back to _sdk_get_access_token() (the only thing the original code did). This is not a reversal of order but entirely new functionality to fix stale token issues.
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 8774 @jlowin, you're absolutely right, and thank you for the correction! Looking at the diff, I can now see clearly that: Original code: access_token: _SDKAccessToken | None = _sdk_get_access_token()New code:
This is indeed new functionality that introduces the request scope check, which never existed before. The original implementation only ever called This new approach solves the stale token issue (#1863) by checking the more up-to-date request scope first, ensuring that refreshed tokens in the current HTTP request are used instead of the stale token that may be in the SDK's context variable. ✏️ Learnings added
|
get_access_token()now checksrequest.scope["user"]first before falling back to the SDK'sauth_context_var. This ensures Streamable HTTP sessions return the fresh token from the current request rather than a stale token from session initialization.Fixes #1863