Skip to content

Commit 48f8385

Browse files
committed
Code review feedback
- Use hmac.compare_digest() for secret validation - url decode client_secret_basic username / password - Narrow the `except` clause on `client_secret_basic` validation
1 parent 0b1517e commit 48f8385

File tree

1 file changed

+17
-8
lines changed

1 file changed

+17
-8
lines changed

src/mcp/server/auth/middleware/client_auth.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import base64
2+
import binascii
3+
import hmac
24
import time
35
from typing import Any
6+
from urllib.parse import unquote
47

58
from starlette.requests import Request
69

@@ -58,7 +61,7 @@ async def authenticate_request(self, request: Request) -> OAuthClientInformation
5861
if not client:
5962
raise AuthenticationError("Invalid client_id")
6063

61-
request_client_secret = None
64+
request_client_secret: str | None = None
6265
auth_header = request.headers.get("Authorization", "")
6366

6467
if client.token_endpoint_auth_method == "client_secret_basic":
@@ -72,17 +75,20 @@ async def authenticate_request(self, request: Request) -> OAuthClientInformation
7275
raise ValueError("Invalid Basic auth format")
7376
basic_client_id, request_client_secret = decoded.split(":", 1)
7477

78+
# URL-decode both parts per RFC 6749 Section 2.3.1
79+
basic_client_id = unquote(basic_client_id)
80+
request_client_secret = unquote(request_client_secret)
81+
7582
if basic_client_id != client_id:
7683
raise AuthenticationError("Client ID mismatch in Basic auth")
77-
except AuthenticationError:
78-
raise
79-
except Exception:
84+
except (ValueError, UnicodeDecodeError, binascii.Error):
8085
raise AuthenticationError("Invalid Basic authentication header")
8186

8287
elif client.token_endpoint_auth_method == "client_secret_post":
83-
request_client_secret = form_data.get("client_secret")
84-
if request_client_secret:
85-
request_client_secret = str(request_client_secret)
88+
raw_form_data = form_data.get("client_secret")
89+
# form_data.get() can return a UploadFile or None, so we need to check if it's a string
90+
if isinstance(raw_form_data, str):
91+
request_client_secret = str(raw_form_data)
8692

8793
elif client.token_endpoint_auth_method == "none":
8894
request_client_secret = None
@@ -93,7 +99,10 @@ async def authenticate_request(self, request: Request) -> OAuthClientInformation
9399
if not request_client_secret:
94100
raise AuthenticationError("Client secret is required")
95101

96-
if client.client_secret != request_client_secret:
102+
# hmac.compare_digest requires that both arguments are either bytes or a `str` containing
103+
# only ASCII characters. Since we do not control `request_client_secret`, we encode both
104+
# arguments to bytes.
105+
if not hmac.compare_digest(client.client_secret.encode(), request_client_secret.encode()):
97106
raise AuthenticationError("Invalid client_secret")
98107

99108
if client.client_secret_expires_at and client.client_secret_expires_at < int(time.time()):

0 commit comments

Comments
 (0)