modelcontextprotocol / modelcontextprotocol/python-sdk

Server ClientAuthenticator requires client_id in token body, rejecting valid client_secret_basic requests (RFC 6749 §2.3.1)

Aperta Adatta ai principianti
#3,545 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

v1 v2
Lingua principale
Python
Stelle
24.3k
Fork
4k
Merge medio
1g 1h
PR unite (30g)
31

Descrizione

Summary

The server-side ClientAuthenticator reads client_id only from the token request form body and raises invalid_client: "Missing client_id" when it is absent — even when the client authenticated correctly via HTTP Basic (Authorization: Basic base64(client_id:client_secret)). Per RFC 6749 §2.3.1, a client using client_secret_basic sends its client_id and client_secret in the Authorization header and MAY omit them from the body. Servers advertising client_secret_basic in token_endpoint_auth_methods_supported therefore reject spec-compliant clients (e.g. clients that use Basic and do not duplicate client_id into the body).

Location

src/mcp/server/auth/middleware/client_auth.py, authenticate_request:

form_data = await request.form()
client_id = form_data.get("client_id")
if not client_id:
    raise AuthenticationError("Missing client_id")
...
if client.token_endpoint_auth_method == "client_secret_basic":
    ...
    basic_client_id, request_client_secret = decoded.split(":", 1)
    basic_client_id = unquote(basic_client_id)
    if basic_client_id != client_id:   # only cross-checks; never used as fallback
        raise AuthenticationError("Client ID mismatch in Basic auth")

client_id from the Basic header is only used to cross-check a body-supplied value; it is never used as a fallback source. So a request with credentials solely in the Basic header fails before the client is even looked up.

Steps to reproduce
  1. Register a client with token_endpoint_auth_method=client_secret_basic.
  2. POST /token with grant_type=authorization_code, Authorization: Basic base64(client_id:client_secret), and no client_id/client_secret in the form body.
  3. Response: 401 {"error":"invalid_client","error_description":"Missing client_id"}.

Sending the same credentials via client_secret_post (in the body) works.

Expected

When Authorization: Basic is present, the authenticator should derive client_id from the header if it is absent from the body (RFC 6749 §2.3.1), then verify the secret as it does today.

Notes

This is the mirror image of client-side PR #3536 (which stops MCP clients from putting client_id in the body under client_secret_basic). With that client-side change, compliant clients will send client_id only in the Basic header — which this server-side code rejects. The two need to agree.

Observed on mcp 1.27.0.

Guida per i contributori

Apri la guida per i contributori

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.

Direzione di ricerca

Inizia in src/mcp/server/auth/middleware/client_auth.py, in authenticate_request, e traccia come vengono analizzati i campi del modulo e le credenziali HTTP Basic. Riproduci la richiesta del token con le credenziali esclusivamente nell’header Authorization, quindi verifica che l’autenticazione client_secret_basic abbia esito positivo, mentre credenziali non corrispondenti continuino a produrre l’errore di autenticazione documentato.

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

Valutazione

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

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.