1+ from contextvars import ContextVar
12import logging
2- from typing import List , Literal , Optional , Union
3+ from typing import Any , Callable , List , Literal , Optional , Union
34
45from .middleware .create_bearer_auth import BearerAuthConfig
5- from .types import VerifyAccessTokenFunction
6- from .config import AuthServerConfig
6+ from .types import AuthInfo , VerifyAccessTokenFunction
7+ from .config import AuthServerConfig , ServerMetadataPaths
78from .exceptions import MCPAuthAuthServerException , AuthServerExceptionCode
89from .utils import validate_server_config
910from starlette .middleware .base import BaseHTTPMiddleware
10- from starlette .responses import JSONResponse
11+ from starlette .responses import Response , JSONResponse
12+ from starlette .requests import Request
13+ from starlette .routing import Route
14+
15+ _context_var_name = "mcp_auth_context"
1116
1217
1318class MCPAuth :
@@ -18,9 +23,22 @@ class MCPAuth:
1823 See Also: https://mcp-auth.dev for more information about the library and its usage.
1924 """
2025
21- def __init__ (self , server : AuthServerConfig ):
26+ server : AuthServerConfig
27+ """
28+ The configuration for the remote authorization server.
29+ """
30+
31+ def __init__ (
32+ self ,
33+ server : AuthServerConfig ,
34+ context_var : ContextVar [Optional [AuthInfo ]] = ContextVar (
35+ _context_var_name , default = None
36+ ),
37+ ):
2238 """
2339 :param server: Configuration for the remote authorization server.
40+ :param context_var: Context variable to store the `AuthInfo` object for the current request.
41+ By default, it will be created with the name "mcp_auth_context".
2442 """
2543
2644 result = validate_server_config (server )
@@ -40,20 +58,78 @@ def __init__(self, server: AuthServerConfig):
4058 logging .warning (f"- { warning } " )
4159
4260 self .server = server
61+ self ._context_var = context_var
62+
63+ @property
64+ def auth_info (self ) -> Optional [AuthInfo ]:
65+ """
66+ The current `AuthInfo` object from the context variable.
67+
68+ This is useful for accessing the authenticated user's information in later middleware or
69+ route handlers.
70+ :return: The current `AuthInfo` object, or `None` if not set.
71+ """
72+
73+ return self ._context_var .get ()
4374
44- def metadata_response (self ) -> JSONResponse :
75+ def metadata_endpoint (self ) -> Callable [[ Request ], Any ] :
4576 """
46- Returns a response containing the server metadata in JSON format with CORS support.
77+ Returns a Starlette endpoint function that handles the OAuth 2.0 Authorization Metadata
78+ endpoint (`/.well-known/oauth-authorization-server`) with CORS support.
79+
80+ Example:
81+ ```python
82+ from starlette.applications import Starlette
83+ from mcpauth import MCPAuth
84+ from mcpauth.config import ServerMetadataPaths
85+
86+ mcp_auth = MCPAuth(server=your_server_config)
87+ app = Starlette(routes=[
88+ Route(
89+ ServerMetadataPaths.OAUTH.value,
90+ mcp_auth.metadata_endpoint(),
91+ methods=["GET", "OPTIONS"] # Ensure to handle both GET and OPTIONS methods
92+ )
93+ ])
94+ ```
95+ """
96+
97+ async def endpoint (request : Request ) -> Response :
98+ if request .method == "OPTIONS" :
99+ response = Response (status_code = 204 )
100+ else :
101+ server_config = self .server
102+ response = JSONResponse (
103+ server_config .metadata .model_dump (exclude_none = True ),
104+ status_code = 200 ,
105+ )
106+ response .headers ["Access-Control-Allow-Origin" ] = "*"
107+ response .headers ["Access-Control-Allow-Methods" ] = "GET, OPTIONS"
108+ response .headers ["Access-Control-Allow-Headers" ] = "*"
109+ return response
110+
111+ return endpoint
112+
113+ def metadata_route (self ) -> Route :
114+ """
115+ Returns a Starlette route that handles the OAuth 2.0 Authorization Metadata endpoint
116+ (`/.well-known/oauth-authorization-server`) with CORS support.
117+
118+ Example:
119+ ```python
120+ from starlette.applications import Starlette
121+ from mcpauth import MCPAuth
122+
123+ mcp_auth = MCPAuth(server=your_server_config)
124+ app = Starlette(routes=[mcp_auth.metadata_route()])
125+ ```
47126 """
48- server_config = self .server
49127
50- response = JSONResponse (
51- server_config .metadata .model_dump (exclude_none = True ),
52- status_code = 200 ,
128+ return Route (
129+ ServerMetadataPaths .OAUTH .value ,
130+ self .metadata_endpoint (),
131+ methods = ["GET" , "OPTIONS" ],
53132 )
54- response .headers ["Access-Control-Allow-Origin" ] = "*"
55- response .headers ["Access-Control-Allow-Methods" ] = "GET, OPTIONS"
56- return response
57133
58134 def bearer_auth_middleware (
59135 self ,
@@ -101,10 +177,11 @@ def bearer_auth_middleware(
101177
102178 return create_bearer_auth (
103179 verify ,
104- BearerAuthConfig (
180+ config = BearerAuthConfig (
105181 issuer = metadata .issuer ,
106182 audience = audience ,
107183 required_scopes = required_scopes ,
108184 show_error_details = show_error_details ,
109185 ),
186+ context_var = self ._context_var ,
110187 )
0 commit comments