Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

CORSMiddleware on /register and /token forwards any non-preflight OPTIONS request straight to the body-reading handler

Aperta Adatta ai principianti
#3,652 2 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
84/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
python
Ambito
api, backend, security

Direzione di ricerca

Inizia da src/mcp/server/auth/routes.py: leggi _cors() (circa righe 57-63) e le definizioni delle route /token e /register (circa 118-136), poi conferma come CORSMiddleware di starlette stabilisca che una richiesta sia preflight. Aggiungi un controllo prima del dispatch affinché un OPTIONS non-preflight (senza origin, o senza l'header access-control-request-method) risponda 405 con un header Allow invece di raggiungere RegistrationHandler/TokenHandler. Riproduci prima con lo script autonomo httpx/ASGITransport dell'issue; per considerare il lavoro concluso, entrambi i casi di OPTIONS non devono più restituire 201/500 e gli auth test esistenti sotto tests/ devono continuare a passare.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug v1 v2

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 in starlette.middleware.cors.CORSMiddleware with allow_methods=["POST","OPTIONS"].
  • routes.py:118-123 and :130-136: both routes list OPTIONS and are wrapped by _cors.
  • starlette/middleware/cors.py:86 (starlette 1.7.0): CORSMiddleware.__call__ only treats the request as a preflight when origin is not None AND method == "OPTIONS" AND "access-control-request-method" in headers; any other OPTIONS (including one with no CORS headers at all) falls through to simple_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 OPTIONS is rejected (405) or answered empty, never reaching the handler.
  • Actual: the empty-body OPTIONS reaches RegistrationHandler.handle, which calls request.json() on an empty body and raises an uncaught JSONDecodeError (unhandled 500), and the JSON-body OPTIONS is parsed, assigned a client_id/client_secret, stored via provider.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.

Lingua principale
Python
Stelle
24.5k
Fork
4k
Merge medio
1g 14h
PR unite (30g)
34

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di modelcontextprotocol/python-sdk

Tutte le issue di modelcontextprotocol/python-sdk

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.