Skip to content

Commit a5265cd

Browse files
committed
cleaning up
1 parent 27f668a commit a5265cd

6 files changed

Lines changed: 29 additions & 28 deletions

File tree

examples/example-fastmcp-mcp/src/auth0/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
including token verification, middleware, and scoped tool decorators.
66
"""
77

8+
from __future__ import annotations
9+
810
import logging
911
import os
1012
from typing import Callable, Union
@@ -30,7 +32,7 @@ def __init__(self, name: str, audience: str, domain: str):
3032
if not self.audience or not self.domain:
3133
raise RuntimeError("audience and domain must be provided")
3234
self.mcp = FastMCP(
33-
name="Auth0 Protected MCP Server",
35+
name=self.name,
3436
stateless_http=True,
3537
)
3638
self._scopes_supported = {
@@ -111,9 +113,9 @@ def _build_www_authenticate_header(self, error_code: str, description: str, incl
111113
Build WWW-Authenticate header according to RFC 9728 Section 5.1.
112114
"""
113115
www_auth_params = [f'error="{error_code}"', f'error_description="{description}"']
114-
115-
if include_resource_metadata:
116-
metadata_url = f"{os.getenv('MCP_SERVER_URL')}/.well-known/oauth-protected-resource"
116+
metadata_url = os.getenv('MCP_SERVER_URL')
117+
if include_resource_metadata and metadata_url:
118+
metadata_url = metadata_url.rstrip("/") + "/.well-known/oauth-protected-resource"
117119
www_auth_params.append(f'resource_metadata="{metadata_url}"')
118120

119121
return f"Bearer {', '.join(www_auth_params)}"

examples/example-fastmcp-mcp/src/auth0/authz.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from __future__ import annotations
22

3-
import asyncio
43
from collections.abc import Iterable
54
from functools import wraps
65

@@ -16,7 +15,7 @@ def require_scopes(required_scopes: Iterable[str]):
1615
Example:
1716
@mcp.tool(...)
1817
@require_scopes(["tool:greet", "tool:whoami"])
19-
def my_tool(name: str, ctx: Context) -> str:
18+
async def my_tool(name: str, ctx: Context) -> str:
2019
return f"Hello {name}!"
2120
"""
2221
required_scopes_list = list(required_scopes)
@@ -37,10 +36,6 @@ async def wrapper(*args, **kwargs):
3736
if missing_scopes:
3837
raise InsufficientScope(f"Missing required scopes: {missing_scopes}")
3938

40-
# Call the original function
41-
if asyncio.iscoroutinefunction(func):
42-
return await func(*args, **kwargs)
43-
else:
44-
return func(*args, **kwargs)
39+
return await func(*args, **kwargs)
4540
return wrapper
4641
return decorator

examples/example-fastmcp-mcp/src/auth0/errors.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from __future__ import annotations
2+
3+
14
class AuthenticationRequired(Exception):
25
"""
36
Raised when authentication is required but missing.

examples/example-fastmcp-mcp/src/auth0/middleware.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,34 @@ async def dispatch(self, request: Request, call_next):
2929
# Extract Authorization header
3030
auth_header = request.headers.get("authorization")
3131
if not auth_header:
32-
raise AuthenticationRequired("Missing Authorization header")
32+
raise MalformedAuthorizationRequest("Missing Authorization header")
3333
if not auth_header.lower().startswith("bearer "):
3434
raise MalformedAuthorizationRequest("Invalid Authorization header format")
3535

3636
# Extract and verify token
37-
token = auth_header[7:] # Remove "Bearer " prefix
37+
token = auth_header[7:].strip() # Remove "Bearer " prefix
3838
try:
3939
decoded_and_verified_token = await self.client.verify_access_token(
4040
token,
4141
required_claims=["sub"]
4242
)
4343

4444
# Check for client_id or azp
45-
clientId = decoded_and_verified_token.get('client_id') or decoded_and_verified_token.get('azp')
46-
if not clientId:
45+
client_id = decoded_and_verified_token.get('client_id') or decoded_and_verified_token.get('azp')
46+
if not client_id:
4747
raise VerifyAccessTokenError("Token is missing 'client_id' or 'azp' claim")
4848

4949
# Set up authentication context
5050
auth_data = {
51-
"client_id": clientId,
51+
"client_id": client_id,
5252
"scopes": decoded_and_verified_token.get("scope", "").split()
5353
if decoded_and_verified_token.get("scope") else []
5454
}
5555

5656
if decoded_and_verified_token.get('exp'):
57-
auth_data["expiresAt"] = decoded_and_verified_token.get('exp')
57+
auth_data["expires_at"] = decoded_and_verified_token.get('exp')
5858

59-
extra = {"sub": decoded_and_verified_token.get('sub'), "client_id": clientId}
59+
extra = {"sub": decoded_and_verified_token.get('sub'), "client_id": client_id}
6060

6161
for field in ['azp', 'name', 'email']:
6262
if decoded_and_verified_token.get(field):
@@ -66,10 +66,9 @@ async def dispatch(self, request: Request, call_next):
6666
request.state.auth = auth_data
6767

6868
return await call_next(request)
69-
except VerifyAccessTokenError as e:
70-
logger.error(f"Token verification failed: {str(e)}")
69+
except VerifyAccessTokenError:
70+
logger.info("Token verification failed")
7171
raise AuthenticationRequired("Invalid token")
72-
except Exception as e:
73-
logger.error(f"Unexpected error in middleware: {str(e)}")
74-
# Re-raise unexpected errors to be handled by generic exception handler
72+
except Exception:
73+
logger.exception("Unexpected error in middleware")
7574
raise

examples/example-fastmcp-mcp/src/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import contextlib
24
import logging
35
import os

examples/example-fastmcp-mcp/src/tools.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from json import dumps as jsonDumps
1+
import json
22

33
from mcp.server.fastmcp import Context
44

@@ -15,7 +15,7 @@ def register_tools(auth0Mcp):
1515

1616
# Tool without required scopes
1717
@mcp.tool()
18-
def echo(text: str) -> str:
18+
async def echo(text: str) -> str:
1919
"""Echoes the input text"""
2020
return text
2121

@@ -27,7 +27,7 @@ def echo(text: str) -> str:
2727
annotations={"readOnlyHint": True}
2828
)
2929
@require_scopes(["tool:greet"])
30-
def greet(name: str, ctx: Context) -> str:
30+
async def greet(name: str, ctx: Context) -> str:
3131
name = (name or "").strip() or "world"
3232
auth_info = ctx.request_context.request.state.auth
3333
user_id = auth_info.get("extra", {}).get("sub")
@@ -41,11 +41,11 @@ def greet(name: str, ctx: Context) -> str:
4141
annotations={"readOnlyHint": True}
4242
)
4343
@require_scopes(["tool:whoami"])
44-
def whoami(ctx: Context) -> str:
44+
async def whoami(ctx: Context) -> str:
4545
auth_info = ctx.request_context.request.state.auth
4646

4747
response_data = {
4848
"user": auth_info.get("extra", {}),
4949
"scopes": auth_info.get("scopes", []),
5050
}
51-
return jsonDumps(response_data, indent=2)
51+
return json.dumps(response_data, indent=2)

0 commit comments

Comments
 (0)