11import base64
2+ import binascii
3+ import hmac
24import time
35from typing import Any
6+ from urllib .parse import unquote
47
58from 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