Content-Length: 283191 | pFad | http://github.com/modelcontextprotocol/python-sdk/pull/2664

6B fix(auth): accept client credentials from Basic auth header in token endpoint (#1315) by afischh · Pull Request #2664 · modelcontextprotocol/python-sdk · GitHub
Skip to content

fix(auth): accept client credentials from Basic auth header in token endpoint (#1315)#2664

Open
afischh wants to merge 1 commit into
modelcontextprotocol:mainfrom
afischh:fix/1315-oauth-basic-auth-header
Open

fix(auth): accept client credentials from Basic auth header in token endpoint (#1315)#2664
afischh wants to merge 1 commit into
modelcontextprotocol:mainfrom
afischh:fix/1315-oauth-basic-auth-header

Conversation

@afischh
Copy link
Copy Markdown

@afischh afischh commented May 23, 2026

Problem

Fixes #1315.

When a client uses HTTP Basic authentication per RFC 6749 §2.3.1, its client_id and client_secret arrive in the Authorization header rather than the request body. Two failure paths caused this to break:

  1. ClientAuthenticator read client_id from form data first, and raised AuthenticationError("Missing client_id") before it even reached the Basic auth parsing branch.

  2. TokenHandler validated form_data against TokenRequest (which requires client_id), so even if authentication had succeeded, the Pydantic model would reject the request.

Fix

client_auth.py — before the Missing client_id guard, attempt to extract client_id from a Basic Authorization header if it is absent from form data:

if not client_id:
    auth_header = request.headers.get("Authorization", "")
    if auth_header.startswith("Basic "):
        try:
            decoded = base64.b64decode(auth_header[6:]).decode("utf-8")
            if ":" in decoded:
                client_id = unquote(decoded.split(":", 1)[0])
        except (ValueError, UnicodeDecodeError, binascii.Error):
            pass

token.py — after client_authenticator successfully authenticates the request (so client_info is already verified), populate client_id from client_info when absent from form data:

form_data = dict(await request.form())
if "client_id" not in form_data:
    form_data["client_id"] = client_info.client_id

This is safe because client_info is the result of successful authentication — we already know who the client is.

Tests

Two new tests in test_auth_integration.py:

  • test_basic_auth_without_client_id_in_bodyauthorization_code grant with client_id only in Authorization header → 200 OK with access token
  • test_basic_auth_refresh_token_without_client_id_in_bodyrefresh_token grant with client_id only in Authorization header → 200 OK with new access token

All 66 existing auth tests continue to pass.

🤖 Generated with Claude Code

…endpoint

When a client uses HTTP Basic authentication (RFC 6749 §2.3.1), its
client_id and client_secret arrive in the Authorization header rather
than the request body. ClientAuthenticator already validates Basic auth
correctly for `client_secret_basic` clients, but it failed early with
"Missing client_id" when client_id was absent from form data.

TokenHandler also required client_id in form data for TokenRequest
validation, causing a second failure path.

Changes:
- ClientAuthenticator.authenticate_request: extract client_id from Basic
  auth header when not present in form body, before the missing-id check
- TokenHandler.handle: populate client_id from the already-authenticated
  client_info when absent from form data, so TokenRequest validates cleanly

Two new tests cover the authorization_code and refresh_token grant flows
with client_id supplied only via Authorization header.

Fixes modelcontextprotocol#1315

Signed-off-by: afischh <afischh@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth TokenHandler should check Authorization header for client credentials

1 participant









ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/modelcontextprotocol/python-sdk/pull/2664

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy