Skip to content

OAuth: 403 responses without insufficient_scope incorrectly retry with same token #1602

Description

@maxisbey

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

elifresponse.status_code==403:
error=self._extract_field_from_www_auth(response, "error")
# Only performs step-up if error == "insufficient_scope"iferror=="insufficient_scope":
self._select_scopes(response)
token_response=yieldawaitself._perform_authorization()
awaitself._handle_token_response(token_response)
# BUG: Retries unconditionally, even when no new tokens were obtainedself._add_auth_header(request)
yieldrequest

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

Expected vs Actual Behavior

ScenarioExpectedActual
403 with insufficient_scopeGet 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 fieldRaise 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:

elifresponse.status_code==403:
error=self._extract_field_from_www_auth(response, "error")
iferror=="insufficient_scope":
try:
self._select_scopes(response)
token_response=yieldawaitself._perform_authorization()
awaitself._handle_token_response(token_response)
# Retry with new tokensself._add_auth_header(request)
yieldrequestexceptException:
logger.exception("OAuth flow error")
raiseelse:
# Permanent authorization failure - cannot be resolved by retryraiseOAuthFlowError(
f"Access forbidden: {erroror'insufficient permissions'}"
)

References


Authored by Claude, reviewed by @maxisbey

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesauthIssues and PRs related to Authentication / OAuth

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions