init
This commit is contained in:
42
mcp/didatodolist-mcp/utils/asgi_auth.py
Normal file
42
mcp/didatodolist-mcp/utils/asgi_auth.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""
|
||||
简单的 ASGI 中间件:为 SSE 入口添加 x-api-key 鉴权。
|
||||
当前 fastmcp 版本不支持 authenticate 参数时,可通过该中间件实现最小可用的 Header 鉴权。
|
||||
"""
|
||||
|
||||
from typing import Callable, Awaitable
|
||||
import os
|
||||
|
||||
|
||||
class ApiKeyAuthMiddleware:
|
||||
def __init__(self, app, header_name: str = "x-api-key", expected_key: str | None = None, sse_path: str = "/sse"):
|
||||
self.app = app
|
||||
self.header_name = header_name.lower()
|
||||
self.expected_key = expected_key or os.environ.get("MCP_API_KEY", "123")
|
||||
self.sse_path = sse_path
|
||||
|
||||
async def __call__(self, scope, receive, send):
|
||||
# 仅对 HTTP scope 且 SSE 路径做校验,其余直接放行
|
||||
if scope.get("type") == "http":
|
||||
path = scope.get("path", "")
|
||||
if path.startswith(self.sse_path):
|
||||
# 提取 header(bytes)
|
||||
headers = {k.decode("latin1").lower(): v.decode("latin1") for k, v in scope.get("headers", [])}
|
||||
api_key = headers.get(self.header_name)
|
||||
if api_key != self.expected_key:
|
||||
body = b"Unauthorized: missing or invalid x-api-key"
|
||||
await send({
|
||||
"type": "http.response.start",
|
||||
"status": 401,
|
||||
"headers": [(b"content-type", b"text/plain; charset=utf-8")],
|
||||
})
|
||||
await send({
|
||||
"type": "http.response.body",
|
||||
"body": body,
|
||||
"more_body": False,
|
||||
})
|
||||
return
|
||||
return await self.app(scope, receive, send)
|
||||
|
||||
|
||||
def with_api_key_auth(app, header_name: str = "x-api-key", expected_key: str | None = None, sse_path: str = "/sse"):
|
||||
return ApiKeyAuthMiddleware(app, header_name=header_name, expected_key=expected_key, sse_path=sse_path)
|
||||
Reference in New Issue
Block a user