Skip to content

Commit d087f90

Browse files
committed
Refactor authentication middleware and routes
- Updated RequireAuthMiddleware to accept an optional resource_metadata_url parameter for enhanced error handling. - Adjusted create_auth_routes to include resource_server_url and resource_name parameters. - Modified OAuthProtectedResourceMetadata to change resource_documentation type to AnyHttpUrl. - Updated tests to reflect changes in resource_server_url and resource_name parameters. Signed-off-by: Xin Fu <xfu83@bloomberg.net>
1 parent f2876ea commit d087f90

File tree

6 files changed

+21
-7
lines changed

6 files changed

+21
-7
lines changed

src/mcp/client/session_group.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,6 @@ async def __aexit__(
154154
for exit_stack in self._session_exit_stacks.values():
155155
tg.start_soon(exit_stack.aclose)
156156

157-
158157
@property
159158
def sessions(self) -> list[mcp.ClientSession]:
160159
"""Returns the list of sessions being managed."""

src/mcp/server/auth/middleware/bearer_auth.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,25 +67,36 @@ class RequireAuthMiddleware:
6767
auth info in the request state.
6868
"""
6969

70-
def __init__(self, app: Any, required_scopes: list[str]):
70+
def __init__(
71+
self,
72+
app: Any,
73+
required_scopes: list[str] | None = None,
74+
resource_metadata_url: str | None = None,
75+
):
7176
"""
7277
Initialize the middleware.
7378
7479
Args:
7580
app: ASGI application
76-
provider: Authentication provider to validate tokens
7781
required_scopes: Optional list of scopes that the token must have
82+
resource_metadata_url: Optional resource metadata URL
7883
"""
7984
self.app = app
8085
self.required_scopes = required_scopes
86+
self.resource_metadata_url = resource_metadata_url
8187

8288
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
8389
auth_user = scope.get("user")
8490
if not isinstance(auth_user, AuthenticatedUser):
85-
raise HTTPException(status_code=401, detail="Unauthorized")
91+
headers = (
92+
{"WWW-Authenticate": f'Bearer resource="{self.resource_metadata_url}"'}
93+
if self.resource_metadata_url
94+
else None
95+
)
96+
raise HTTPException(status_code=401, detail="Unauthorized", headers=headers)
8697
auth_credentials = scope.get("auth")
8798

88-
for required_scope in self.required_scopes:
99+
for required_scope in self.required_scopes or []:
89100
# auth_credentials should always be provided; this is just paranoia
90101
if (
91102
auth_credentials is None

src/mcp/server/auth/routes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ def create_auth_routes(
9292
authorization_servers=[metadata.issuer],
9393
scopes_supported=metadata.scopes_supported,
9494
resource_name=resource_name,
95-
resource_documentation= service_documentation_url,
95+
resource_documentation=service_documentation_url,
9696
)
9797

9898
# Create routes

src/mcp/server/fastmcp/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,11 @@ async def handle_sse(scope: Scope, receive: Receive, send: Send):
715715
create_auth_routes(
716716
provider=self._auth_server_provider,
717717
issuer_url=self.settings.auth.issuer_url,
718+
resource_server_url=self.settings.auth.resource_server_url,
718719
service_documentation_url=self.settings.auth.service_documentation_url,
719720
client_registration_options=self.settings.auth.client_registration_options,
720721
revocation_options=self.settings.auth.revocation_options,
722+
resource_name=self.settings.auth.resource_name,
721723
)
722724
)
723725

src/mcp/shared/auth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ class OAuthProtectedResourceMetadata(BaseModel):
150150
bearer_methods_supported: list[str] | None = None
151151
resource_signing_alg_values_supported: list[str] | None = None
152152
resource_name: str | None = None
153-
resource_documentation: str | None = None
153+
resource_documentation: AnyHttpUrl | None = None
154154
resource_policy_uri: AnyHttpUrl | None = None
155155
resource_tos_uri: AnyHttpUrl | None = None
156156
tls_client_certificate_bound_access_tokens: bool | None = None

tests/server/auth/test_error_handling.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def app(oauth_provider):
4040
auth_routes = create_auth_routes(
4141
oauth_provider,
4242
issuer_url=AnyHttpUrl("http://localhost"),
43+
resource_server_url=AnyHttpUrl("http://localhost"),
44+
resource_name="Test Resource",
4345
client_registration_options=client_registration_options,
4446
revocation_options=revocation_options,
4547
)

0 commit comments

Comments
 (0)