fix: use FastMCP get_access_token() for OAuth claims extraction

The previous implementation tried to access OAuth token claims via
context.request_context.access_token.claims, which doesn't exist in
FastMCP's context structure.

FastMCP stores access tokens in Python's ContextVars, accessible via
the get_access_token() dependency function. This fix updates both
extract_user_from_context() and RBACMiddleware._extract_user_from_context()
to use this correct approach.

Before: Users appeared as "anonymous" with no groups (RBAC denied all)
After: User identity and groups correctly extracted from OAuth claims
This commit is contained in:
Ryan Malloy 2025-12-28 11:15:28 -07:00
parent ab83c70c31
commit 79d2caef45

View File

@ -92,24 +92,26 @@ def with_permission_check(tool_name: str) -> Callable:
def extract_user_from_context(ctx) -> dict[str, Any] | None: def extract_user_from_context(ctx) -> dict[str, Any] | None:
"""Extract user information from FastMCP context. """Extract user information from FastMCP context.
Uses FastMCP's dependency injection to get the access token from
the current request's context variable.
Args: Args:
ctx: FastMCP Context object. ctx: FastMCP Context object (unused, kept for API compatibility).
Returns: Returns:
User info dict from OAuth token claims, or None if not authenticated. User info dict from OAuth token claims, or None if not authenticated.
""" """
if ctx is None:
return None
# Try to get access token from context
try: try:
# FastMCP stores the access token in request_context # FastMCP stores access token in a context variable, accessed via dependency
if hasattr(ctx, "request_context") and ctx.request_context: from fastmcp.server.dependencies import get_access_token
token = getattr(ctx.request_context, "access_token", None)
if token and hasattr(token, "claims"): access_token = get_access_token()
return token.claims if access_token and hasattr(access_token, "claims"):
except Exception: return access_token.claims
pass except (RuntimeError, ImportError) as e:
# RuntimeError: No active HTTP request context
# ImportError: FastMCP auth dependencies not available
logger.debug("Could not get access token: %s", e)
return None return None
@ -208,23 +210,26 @@ class RBACMiddleware(Middleware):
) -> dict[str, Any] | None: ) -> dict[str, Any] | None:
"""Extract user claims from FastMCP context. """Extract user claims from FastMCP context.
Uses FastMCP's dependency injection to retrieve the access token
from the current request's context variable.
Args: Args:
fastmcp_ctx: FastMCP Context object from middleware context. fastmcp_ctx: FastMCP Context object (unused, kept for API compatibility).
Returns: Returns:
User claims dict from OAuth token, or None if not authenticated. User claims dict from OAuth token, or None if not authenticated.
""" """
if fastmcp_ctx is None:
return None
try: try:
# FastMCP stores access token in request_context # FastMCP stores access token in a context variable, accessed via dependency
if hasattr(fastmcp_ctx, "request_context") and fastmcp_ctx.request_context: from fastmcp.server.dependencies import get_access_token
token = getattr(fastmcp_ctx.request_context, "access_token", None)
if token and hasattr(token, "claims"): access_token = get_access_token()
return token.claims if access_token and hasattr(access_token, "claims"):
except Exception as e: return access_token.claims
logger.debug("Failed to extract user from context: %s", e) except (RuntimeError, ImportError) as e:
# RuntimeError: No active HTTP request context
# ImportError: FastMCP auth dependencies not available
logger.debug("Could not get access token: %s", e)
return None return None