Skip to content

Commit a1307ab

Browse files
yabeayangben
and
yangben
authored
Fix the issue of get Authorization header fails during bearer auth (#637)
Co-authored-by: yangben <[email protected]>
1 parent 9d99aee commit a1307ab

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,15 @@ def __init__(
3434
self.provider = provider
3535

3636
async def authenticate(self, conn: HTTPConnection):
37-
auth_header = conn.headers.get("Authorization")
38-
if not auth_header or not auth_header.startswith("Bearer "):
37+
auth_header = next(
38+
(
39+
conn.headers.get(key)
40+
for key in conn.headers
41+
if key.lower() == "authorization"
42+
),
43+
None,
44+
)
45+
if not auth_header or not auth_header.lower().startswith("bearer "):
3946
return None
4047

4148
token = auth_header[7:] # Remove "Bearer " prefix

tests/server/auth/middleware/test_bearer_auth.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import pytest
99
from starlette.authentication import AuthCredentials
10+
from starlette.datastructures import Headers
1011
from starlette.exceptions import HTTPException
1112
from starlette.requests import Request
1213
from starlette.types import Message, Receive, Scope, Send
@@ -221,6 +222,66 @@ async def test_token_without_expiry(
221222
assert user.access_token == no_expiry_access_token
222223
assert user.scopes == ["read", "write"]
223224

225+
async def test_lowercase_bearer_prefix(
226+
self,
227+
mock_oauth_provider: OAuthAuthorizationServerProvider[Any, Any, Any],
228+
valid_access_token: AccessToken,
229+
):
230+
"""Test with lowercase 'bearer' prefix in Authorization header"""
231+
backend = BearerAuthBackend(provider=mock_oauth_provider)
232+
add_token_to_provider(mock_oauth_provider, "valid_token", valid_access_token)
233+
headers = Headers({"Authorization": "bearer valid_token"})
234+
scope = {"type": "http", "headers": headers.raw}
235+
request = Request(scope)
236+
result = await backend.authenticate(request)
237+
assert result is not None
238+
credentials, user = result
239+
assert isinstance(credentials, AuthCredentials)
240+
assert isinstance(user, AuthenticatedUser)
241+
assert credentials.scopes == ["read", "write"]
242+
assert user.display_name == "test_client"
243+
assert user.access_token == valid_access_token
244+
245+
async def test_mixed_case_bearer_prefix(
246+
self,
247+
mock_oauth_provider: OAuthAuthorizationServerProvider[Any, Any, Any],
248+
valid_access_token: AccessToken,
249+
):
250+
"""Test with mixed 'BeArEr' prefix in Authorization header"""
251+
backend = BearerAuthBackend(provider=mock_oauth_provider)
252+
add_token_to_provider(mock_oauth_provider, "valid_token", valid_access_token)
253+
headers = Headers({"authorization": "BeArEr valid_token"})
254+
scope = {"type": "http", "headers": headers.raw}
255+
request = Request(scope)
256+
result = await backend.authenticate(request)
257+
assert result is not None
258+
credentials, user = result
259+
assert isinstance(credentials, AuthCredentials)
260+
assert isinstance(user, AuthenticatedUser)
261+
assert credentials.scopes == ["read", "write"]
262+
assert user.display_name == "test_client"
263+
assert user.access_token == valid_access_token
264+
265+
async def test_mixed_case_authorization_header(
266+
self,
267+
mock_oauth_provider: OAuthAuthorizationServerProvider[Any, Any, Any],
268+
valid_access_token: AccessToken,
269+
):
270+
"""Test authentication with mixed 'Authorization' header."""
271+
backend = BearerAuthBackend(provider=mock_oauth_provider)
272+
add_token_to_provider(mock_oauth_provider, "valid_token", valid_access_token)
273+
headers = Headers({"AuThOrIzAtIoN": "BeArEr valid_token"})
274+
scope = {"type": "http", "headers": headers.raw}
275+
request = Request(scope)
276+
result = await backend.authenticate(request)
277+
assert result is not None
278+
credentials, user = result
279+
assert isinstance(credentials, AuthCredentials)
280+
assert isinstance(user, AuthenticatedUser)
281+
assert credentials.scopes == ["read", "write"]
282+
assert user.display_name == "test_client"
283+
assert user.access_token == valid_access_token
284+
224285

225286
@pytest.mark.anyio
226287
class TestRequireAuthMiddleware:

0 commit comments

Comments
 (0)