modelcontextprotocol / modelcontextprotocol/python-sdk

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

Aberta Para iniciantes
#3,545 1 comentário 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

v1 v2
Linguagem predominante
Python
Estrelas
24.3k
Forks
4k
Merge médio
1d 1h
PRs com merge (30d)
31

Descrição

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.

Guia de contribuição

Abrir o guia de contribuição

Primeiros passos

  1. Leia a issue inteira e depois o guia de contribuição do projeto.
  2. Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
  3. Faça um fork do repositório e trabalhe em uma branch.
  4. Abra um pull request que referencie o número da issue.

Direção de pesquisa

Comece em src/mcp/server/auth/middleware/client_auth.py, em authenticate_request, e acompanhe como os campos do formulário e as credenciais HTTP Basic são analisados. Reproduza a requisição do token com as credenciais somente no cabeçalho Authorization e, em seguida, verifique se a autenticação client_secret_basic é bem-sucedida, enquanto credenciais incompatíveis continuam produzindo o erro de autenticação documentado.

Escrita pelo modelo de indexação a partir do texto da issue.

Avaliação

Stack de tecnologia
python
Domínio
api, authentication
Tipo de issue
Bug
Dificuldade
2/5
Tempo estimado
1-3 horas
Status de atividade
Ativa
Clareza
Claramente especificada
Facilidade para iniciantes
76/100

Receba novas issues na sua caixa de entrada

Um resumo curto de issues do GitHub para quem está começando.