modelcontextprotocol / modelcontextprotocol/python-sdk

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

Ouverte Adaptée aux débutants
#3,545 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

v1 v2
Langage dominant
Python
Étoiles
24.3k
Forks
4k
Merge moyen
1 j 1 h
PR mergées (30 j)
31

Description

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.

Guide de contribution

Ouvrir le guide de contribution

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Piste de recherche

Commencez dans src/mcp/server/auth/middleware/client_auth.py au niveau de authenticate_request et suivez la manière dont les champs du formulaire et les identifiants HTTP Basic sont analysés. Reproduisez la requête de token avec les identifiants uniquement dans l’en-tête Authorization, puis vérifiez que l’authentification client_secret_basic réussit, tandis que des identifiants incohérents produisent toujours l’erreur d’authentification documentée.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Évaluation

Stack technique
python
Domaine
api, authentication
Type d'issue
Bug
Difficulté
2/5
Temps estimé
1-3 heures
Activité
Active
Clarté
Clairement spécifiée
Accessibilité débutants
76/100

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.