CORSMiddleware on /register and /token forwards any non-preflight OPTIONS request straight to the body-reading handler
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
从 src/mcp/server/auth/routes.py 开始:阅读 _cors()(约 57-63 行)以及 /token 和 /register 的路由定义(约 118-136 行),然后确认 starlette 的 CORSMiddleware 如何判定一个请求是 preflight。在 dispatch 之前加一个守卫,使非 preflight 的 OPTIONS(没有 origin,或没有 access-control-request-method 头)返回 405 和 Allow 头,而不是到达 RegistrationHandler/TokenHandler。先用 issue 中独立的 httpx/ASGITransport 脚本复现;完成的标志是两种 OPTIONS 情况都不再返回 201/500,且 tests/ 下现有的认证测试仍然通过。
由索引模型根据 Issue 内容生成。
描述
Affected versions: mcp 1.30.0 (confirmed); still present in the latest release, mcp 2.3.0, same code at src/mcp/server/auth/routes.py:119-120 (/token) and :132-136 (/register).
Where
routes.py:57-63:_cors()wraps the handler instarlette.middleware.cors.CORSMiddlewarewithallow_methods=["POST","OPTIONS"].routes.py:118-123and:130-136: both routes listOPTIONSand are wrapped by_cors.starlette/middleware/cors.py:86(starlette 1.7.0):CORSMiddleware.__call__only treats the request as a preflight whenorigin is not NoneANDmethod == "OPTIONS"AND"access-control-request-method" in headers; any otherOPTIONS(including one with no CORS headers at all) falls through tosimple_response, which calls the wrapped handler.
Repro (standalone, mcp + starlette + httpx only)
import anyio
from pydantic import AnyHttpUrl
from starlette.applications import Starlette
from mcp.server.auth.routes import create_auth_routes
from mcp.server.auth.settings import ClientRegistrationOptions
from httpx import ASGITransport, AsyncClient
class DummyProvider:
def __init__(self): self.clients = {}
async def register_client(self, client_info): self.clients[client_info.client_id] = client_info
async def get_client(self, client_id): return self.clients.get(client_id)
async def authorize(self, *a, **k): ...
async def load_authorization_code(self, *a, **k): ...
async def exchange_authorization_code(self, *a, **k): ...
async def load_refresh_token(self, *a, **k): ...
async def exchange_refresh_token(self, *a, **k): ...
async def revoke_token(self, *a, **k): ...
async def main():
provider = DummyProvider()
routes = create_auth_routes(provider, issuer_url=AnyHttpUrl("https://example.test"), client_registration_options=ClientRegistrationOptions(enabled=True))
app = Starlette(routes=routes)
transport = ASGITransport(app=app, raise_app_exceptions=False)
async with AsyncClient(transport=transport, base_url="https://example.test") as client:
r1 = await client.options("/register")
print("OPTIONS /register, no body, no CORS headers ->", r1.status_code)
r2 = await client.request("OPTIONS", "/register", content=b'{"redirect_uris": ["https://client.example/cb"], "client_name": "demo"}', headers={"Content-Type": "application/json"})
print("OPTIONS /register, JSON body, no CORS headers ->", r2.status_code, r2.text[:200])
print("Clients stored:", list(provider.clients.keys()))
anyio.run(main)
Expected vs actual
- Expected: a non-preflight
OPTIONSis rejected (405) or answered empty, never reaching the handler. - Actual: the empty-body
OPTIONSreachesRegistrationHandler.handle, which callsrequest.json()on an empty body and raises an uncaughtJSONDecodeError(unhandled 500), and the JSON-bodyOPTIONSis parsed, assigned aclient_id/client_secret, stored viaprovider.register_client, and answered 201 — a registration created over what looked like a CORS preflight, bypassing rate limiting.
Suggested fix
Check origin is not None and "access-control-request-method" in headers before dispatch, in create_auth_routes/cors_middleware, answering a non-preflight OPTIONS with 405 + Allow header; no Starlette change required.
- 主要语言
- Python
- 星标
- 24.5k
- 派生
- 4k
- 平均合并
- 1 天 14 小时
- 30 天内合并 PR
- 34
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
modelcontextprotocol/python-sdk 的其他 Issue
-
bug v1 v2
难度 1/5 1 小时以内 新手友好度 85/100
modelcontextprotocol/python-sdk#3656 ·
维护者通常 1 天内回复
-
enhancement
难度 2/5 1-3 小时 新手友好度 66/100
modelcontextprotocol/python-sdk#3655 ·
维护者通常 1 天内回复
-
enhancement
难度 1/5 1 小时以内 新手友好度 86/100
modelcontextprotocol/python-sdk#3654 ·
维护者通常 1 天内回复
-
bug spec-2026-07-28 v2
难度 2/5 1-3 小时 新手友好度 88/100
modelcontextprotocol/python-sdk#3649 ·
维护者通常 1 天内回复
-
bug v1 v2
难度 2/5 1-3 小时 新手友好度 75/100
modelcontextprotocol/python-sdk#3639 · 1 条评论 ·
维护者通常 1 天内回复
查看 modelcontextprotocol/python-sdk 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 85/100
维护者通常 1 天内回复
-
SR_SECURITY_DESCRIPTOR.fromString drops the SACL when no DACL is present可能已有人在做 @paul7436 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 2 天内回复
-
难度 2/5 1-3 小时 新手友好度 85/100
equinor/fmu-sumo-uploader#302 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 75/100
modelscope/evalscope#1821 ·
维护者通常 1 天内回复
-
Sanity on ansible-core devel fails: ignore-2.23.txt references the removed import-3.9 test可能已有人在做 @yurnov 今天认领。 未关闭needs_triage
难度 1/5 1 小时以内 新手友好度 91/100
ansible-collections/kubernetes.core#1275 ·
维护者通常 1 天内回复