From a962f2189c3ee904bbac11837d1f37b4baabfc8d Mon Sep 17 00:00:00 2001 From: Yukuan Date: Mon, 27 Oct 2025 15:12:45 +0800 Subject: [PATCH 1/2] Add resource_owner field to AuthorizationCode and AccessToken classes --- .../simple-auth/mcp_simple_auth/simple_auth_provider.py | 3 +++ src/mcp/server/auth/provider.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/examples/servers/simple-auth/mcp_simple_auth/simple_auth_provider.py b/examples/servers/simple-auth/mcp_simple_auth/simple_auth_provider.py index 0f1092d7d..0db39eda8 100644 --- a/examples/servers/simple-auth/mcp_simple_auth/simple_auth_provider.py +++ b/examples/servers/simple-auth/mcp_simple_auth/simple_auth_provider.py @@ -163,6 +163,7 @@ async def handle_simple_callback(self, username: str, password: str, state: str) redirect_uri_provided_explicitly = state_data["redirect_uri_provided_explicitly"] == "True" client_id = state_data["client_id"] resource = state_data.get("resource") # RFC 8707 + resource_owner = username # Use username as resource owner # These are required values from our own state mapping assert redirect_uri is not None @@ -184,6 +185,7 @@ async def handle_simple_callback(self, username: str, password: str, state: str) scopes=[self.settings.mcp_scope], code_challenge=code_challenge, resource=resource, # RFC 8707 + resource_owner=resource_owner, ) self.auth_codes[new_code] = auth_code @@ -220,6 +222,7 @@ async def exchange_authorization_code( scopes=authorization_code.scopes, expires_at=int(time.time()) + 3600, resource=authorization_code.resource, # RFC 8707 + resource_owner=authorization_code.resource_owner, ) # Store user data mapping for this token diff --git a/src/mcp/server/auth/provider.py b/src/mcp/server/auth/provider.py index a7b108602..77b0bebb5 100644 --- a/src/mcp/server/auth/provider.py +++ b/src/mcp/server/auth/provider.py @@ -25,6 +25,7 @@ class AuthorizationCode(BaseModel): redirect_uri: AnyUrl redirect_uri_provided_explicitly: bool resource: str | None = None # RFC 8707 resource indicator + resource_owner: str | None = None class RefreshToken(BaseModel): @@ -40,6 +41,7 @@ class AccessToken(BaseModel): scopes: list[str] expires_at: int | None = None resource: str | None = None # RFC 8707 resource indicator + resource_owner: str | None = None RegistrationErrorCode = Literal[ From 042fa3d9485be8f5e6f90b36fd65cab2e42fe42a Mon Sep 17 00:00:00 2001 From: Yukuan Date: Mon, 27 Oct 2025 16:25:29 +0800 Subject: [PATCH 2/2] Add resource_owner field in the introspection process --- examples/servers/simple-auth/mcp_simple_auth/auth_server.py | 1 + examples/servers/simple-auth/mcp_simple_auth/token_verifier.py | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/servers/simple-auth/mcp_simple_auth/auth_server.py b/examples/servers/simple-auth/mcp_simple_auth/auth_server.py index 80a2e8b8a..4082445db 100644 --- a/examples/servers/simple-auth/mcp_simple_auth/auth_server.py +++ b/examples/servers/simple-auth/mcp_simple_auth/auth_server.py @@ -123,6 +123,7 @@ async def introspect_handler(request: Request) -> Response: "iat": int(time.time()), "token_type": "Bearer", "aud": access_token.resource, # RFC 8707 audience claim + "sub": access_token.resource_owner, # Resource owner } ) diff --git a/examples/servers/simple-auth/mcp_simple_auth/token_verifier.py b/examples/servers/simple-auth/mcp_simple_auth/token_verifier.py index 5228d034e..136754783 100644 --- a/examples/servers/simple-auth/mcp_simple_auth/token_verifier.py +++ b/examples/servers/simple-auth/mcp_simple_auth/token_verifier.py @@ -75,6 +75,7 @@ async def verify_token(self, token: str) -> AccessToken | None: scopes=data.get("scope", "").split() if data.get("scope") else [], expires_at=data.get("exp"), resource=data.get("aud"), # Include resource in token + resource_owner=data.get("sub"), # Use 'sub' claim as resource owner ) except Exception as e: logger.warning(f"Token introspection failed: {e}")