modelcontextprotocol / modelcontextprotocol/python-sdk

OAuth: 403 responses without insufficient_scope incorrectly retry with same token

Aberta
#1,602 0 comentários 0 reações 0 responsáveis Ver no GitHub

Ninguém assumiu esta issue ainda.

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

Descrição

Summary

The OAuth client unconditionally retries all 403 responses, even when the error is not insufficient_scope. This causes an unnecessary retry attempt with the same token that will fail for the same reason.

Location

src/mcp/client/auth/oauth2.py, lines 662-681

The Bug

elif response.status_code == 403:
    error = self._extract_field_from_www_auth(response, "error")
    
    # Only performs step-up if error == "insufficient_scope"
    if error == "insufficient_scope":
        self._select_scopes(response)
        token_response = yield await self._perform_authorization()
        await self._handle_token_response(token_response)
    
    # BUG: Retries unconditionally, even when no new tokens were obtained
    self._add_auth_header(request)
    yield request

Lines 679-681 execute regardless of whether step-up authorization occurred, causing a retry with the same credentials.

Expected vs Actual Behavior

Scenario Expected Actual
403 with insufficient_scope Get new tokens → retry ✅ Correct
403 with different error (e.g., invalid_token) Raise error immediately ❌ Retries once with same token, then fails
403 with no error field Raise error immediately ❌ Retries once with same token, then fails

Impact

  • Wasted network round-trip: Client makes doomed retry request that will fail for the same reason
  • Poor error feedback: Delays error reporting by one request cycle
  • Spec non-compliance: MCP Authorization Spec implies retry only for insufficient_scope
  • Resource waste: Unnecessary load on server and client

Fix

Move the retry logic inside the if error == "insufficient_scope": block and raise an error otherwise:

elif response.status_code == 403:
    error = self._extract_field_from_www_auth(response, "error")
    
    if error == "insufficient_scope":
        try:
            self._select_scopes(response)
            token_response = yield await self._perform_authorization()
            await self._handle_token_response(token_response)
            
            # Retry with new tokens
            self._add_auth_header(request)
            yield request
        except Exception:
            logger.exception("OAuth flow error")
            raise
    else:
        # Permanent authorization failure - cannot be resolved by retry
        raise OAuthFlowError(
            f"Access forbidden: {error or 'insufficient permissions'}"
        )

References


Authored by Claude, reviewed by @maxisbey

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

Leia src/mcp/client/auth/oauth2.py por volta das linhas 662-681, concentrando-se em como as respostas 403 são tratadas depois que o campo error é extraído. Confirme o comportamento para insufficient_scope, outro erro e nenhum campo error; considera-se concluído quando apenas o caminho scope-challenge tenta novamente e as outras respostas 403 geram uma exceção imediatamente.

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

Avaliação

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

Receba novas issues na sua caixa de entrada

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