Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

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

Abierto Apto para principiantes
#3,652 1 comentario 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
2/5
Tiempo estimado
1-3 horas
Aptitud para principiantes
84/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
python
Área
api, backend, security

Línea de trabajo

Comienza en src/mcp/server/auth/routes.py: lee _cors() (líneas aprox. 57-63) y las definiciones de las rutas /token y /register (líneas aprox. 118-136), y luego confirma cómo CORSMiddleware de starlette decide que una solicitud es preflight. Añade una guarda antes del dispatch para que un OPTIONS que no es preflight (sin origin, o sin el header access-control-request-method) responda 405 con un header Allow en lugar de llegar a RegistrationHandler/TokenHandler. Reproduce primero con el script independiente httpx/ASGITransport del issue; se considera terminado cuando los dos casos de OPTIONS ya no devuelven 201/500 y los tests de auth existentes en tests/ siguen pasando.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

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.

Lenguaje dominante
Python
Estrellas
24.5k
Forks
4k
Merge medio
1 d 13 h
PR fusionados (30 d)
35

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de modelcontextprotocol/python-sdk

Todos los issues de modelcontextprotocol/python-sdk

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.