Skip to content

Commit d817551

Browse files
committed
Implement OAuth / OpenId well known path that simply grabs IdPs
1 parent 4d4310c commit d817551

File tree

2 files changed

+30
-8
lines changed

2 files changed

+30
-8
lines changed

src/mcp/server/auth/backend.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
logger = get_logger(__name__)
2121

22-
WELL_KNOWN_PATH: str = ".well-known/oauth-authorization-server"
22+
OPENID_WELL_KNOWN_PATH: str = ".well-known/openid-configuration"
23+
OAUTH_WELL_KNOWN_PATH: str = ".well-known/oauth-authorization-server"
2324

2425

2526
def on_error(_: HTTPConnection, err: AuthenticationError) -> Response:
@@ -105,7 +106,7 @@ async def authenticate(
105106
# TODO: Cache this stuff
106107
async with httpx.AsyncClient() as client:
107108
issuer_url = str(self.issuer_url).rstrip("/") + "/"
108-
well_known_url = urljoin(issuer_url, WELL_KNOWN_PATH)
109+
well_known_url = urljoin(issuer_url, OAUTH_WELL_KNOWN_PATH)
109110
response = await client.get(well_known_url)
110111

111112
jwks_url = response.json()["jwks_uri"]

src/mcp/server/fastmcp/server.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
)
1313
from itertools import chain
1414
from typing import Any, Generic, Literal
15+
from urllib.parse import urljoin
1516

1617
import anyio
1718
import pydantic_core
@@ -21,9 +22,10 @@
2122
from pydantic_settings import BaseSettings, SettingsConfigDict
2223
from starlette.applications import Starlette
2324
from starlette.requests import Request
25+
from starlette.responses import Response, JSONResponse
2426
from starlette.routing import Mount, Route
2527

26-
from mcp.server.auth.backend import BearerTokenBackend
28+
from mcp.server.auth.backend import BearerTokenBackend, OAUTH_WELL_KNOWN_PATH, OPENID_WELL_KNOWN_PATH
2729
from mcp.server.fastmcp.exceptions import ResourceError
2830
from mcp.server.fastmcp.prompts import Prompt, PromptManager
2931
from mcp.server.fastmcp.resources import FunctionResource, Resource, ResourceManager
@@ -499,19 +501,38 @@ async def handle_sse(request: Request) -> None:
499501
self._mcp_server.create_initialization_options(),
500502
)
501503

504+
import httpx
505+
async def handle_well_known(_: Request) -> Response:
506+
async with httpx.AsyncClient() as client:
507+
issuer_url = str(self.settings.issuer_url).rstrip("/") + "/"
508+
well_known_url = urljoin(issuer_url, OAUTH_WELL_KNOWN_PATH)
509+
response = await client.get(well_known_url)
510+
return JSONResponse(response.json())
511+
512+
routes = [
513+
Route(self.settings.sse_path, endpoint=handle_sse),
514+
Mount(self.settings.message_path, app=sse.handle_post_message),
515+
]
502516
middleware = []
503517

504518
if self.settings.authentication_enabled:
519+
# We wrap all the default routes in the auth check.
505520
backend = BearerTokenBackend(self.settings.issuer_url)
506-
middleware.append(backend.as_middleware())
521+
522+
routes = [
523+
Route(f"/{OAUTH_WELL_KNOWN_PATH}", endpoint=handle_well_known),
524+
Route(f"/{OPENID_WELL_KNOWN_PATH}", endpoint=handle_well_known),
525+
Mount(
526+
"/",
527+
routes=routes,
528+
middleware=[backend.as_middleware()]
529+
),
530+
]
507531

508532
return Starlette(
509533
debug=self.settings.debug,
510534
middleware=middleware,
511-
routes=[
512-
Route(self.settings.sse_path, endpoint=handle_sse),
513-
Mount(self.settings.message_path, app=sse.handle_post_message),
514-
],
535+
routes=routes,
515536
)
516537

517538
async def list_prompts(self) -> list[MCPPrompt]:

0 commit comments

Comments
 (0)