99
1010from starlette .requests import HTTPConnection , Request
1111from starlette .exceptions import HTTPException
12- from starlette .authentication import AuthCredentials , AuthenticationBackend , AuthenticationError , BaseUser , SimpleUser , UnauthenticatedUser
12+ from starlette .authentication import AuthCredentials , AuthenticationBackend , AuthenticationError , BaseUser , SimpleUser , UnauthenticatedUser , has_required_scope
1313from starlette .middleware .authentication import AuthenticationMiddleware
14+ from starlette .types import Scope
1415
1516from mcp .server .auth .errors import InsufficientScopeError , InvalidTokenError , OAuthError
1617from mcp .server .auth .provider import OAuthServerProvider
@@ -34,22 +35,12 @@ class BearerAuthBackend(AuthenticationBackend):
3435 def __init__ (
3536 self ,
3637 provider : OAuthServerProvider ,
37- required_scopes : Optional [List [str ]] = None
3838 ):
39- """
40- Initialize the backend.
41-
42- Args:
43- provider: Authentication provider to validate tokens
44- required_scopes: Optional list of scopes that the token must have
45- """
4639 self .provider = provider
47- self .required_scopes = required_scopes or []
4840
4941 async def authenticate (self , conn : HTTPConnection ):
5042
5143 if "Authorization" not in conn .headers :
52- raise AuthenticationError ()
5344 return None
5445
5546 auth_header = conn .headers ["Authorization" ]
@@ -61,14 +52,7 @@ async def authenticate(self, conn: HTTPConnection):
6152 try :
6253 # Validate the token with the provider
6354 auth_info = await self .provider .verify_access_token (token )
64-
65- # Check if the token has all required scopes
66- if self .required_scopes :
67- has_all_scopes = all (scope in auth_info .scopes for scope in self .required_scopes )
68- if not has_all_scopes :
69- raise InsufficientScopeError ("Insufficient scope" )
70-
71- # Check if the token is expired
55+
7256 if auth_info .expires_at and auth_info .expires_at < int (time .time ()):
7357 raise InvalidTokenError ("Token has expired" )
7458
@@ -79,7 +63,7 @@ async def authenticate(self, conn: HTTPConnection):
7963 return None
8064
8165
82- class BearerAuthMiddleware :
66+ class RequireAuthMiddleware :
8367 """
8468 Middleware that requires a valid Bearer token in the Authorization header.
8569
@@ -92,8 +76,7 @@ class BearerAuthMiddleware:
9276 def __init__ (
9377 self ,
9478 app : Any ,
95- provider : OAuthServerProvider ,
96- required_scopes : Optional [List [str ]] = None
79+ required_scopes : list [str ]
9780 ):
9881 """
9982 Initialize the middleware.
@@ -103,18 +86,15 @@ def __init__(
10386 provider: Authentication provider to validate tokens
10487 required_scopes: Optional list of scopes that the token must have
10588 """
106- self .app = AuthenticationMiddleware (
107- app ,
108- backend = BearerAuthBackend (provider , required_scopes )
109- )
110-
111- async def __call__ (self , scope : Dict , receive : Callable , send : Callable ) -> None :
112- """
113- Process the request and validate the bearer token.
89+ self .app = app
90+ self .required_scopes = required_scopes
91+
92+ async def __call__ (self , scope : Scope , receive : Callable , send : Callable ) -> None :
93+ auth_credentials = scope .get ('auth' )
11494
115- Args :
116- scope: ASGI scope
117- receive: ASGI receive function
118- send: ASGI send function
119- """
95+ for required_scope in self . required_scopes :
96+ # auth_credentials should always be provided; this is just paranoia
97+ if auth_credentials is None or required_scope not in auth_credentials . scopes :
98+ raise HTTPException ( status_code = 403 , detail = "Insufficient scope" )
99+
120100 await self .app (scope , receive , send )
0 commit comments