fix(anthropic): detect oauth Authorization header case-insensitively

This commit is contained in:
mateo-berri 2026-08-26 11:16:17 -07:00
parent c449f11451
commit 2068066d69
2 changed files with 28 additions and 9 deletions

View file

@ -78,8 +78,8 @@ def optionally_handle_anthropic_oauth(headers: dict, api_key: str | None) -> tup
"""
Handle Anthropic OAuth token detection and header setup.
If an OAuth token is detected in the Authorization header, extracts it
and sets the required OAuth headers.
If an OAuth token is detected in the Authorization header (any casing),
extracts it and sets the required OAuth headers.
Args:
headers: Request headers dict
@ -89,16 +89,18 @@ def optionally_handle_anthropic_oauth(headers: dict, api_key: str | None) -> tup
Tuple of (updated headers, api_key)
"""
# Check Authorization header (passthrough / forwarded requests)
auth_header: Final = headers.get("authorization", "")
if auth_header and auth_header.startswith(f"Bearer {ANTHROPIC_OAUTH_TOKEN_PREFIX}"):
api_key = auth_header.replace("Bearer ", "")
headers.pop("x-api-key", None)
auth_header: Final = next((value for name, value in headers.items() if name.lower() == "authorization"), "")
if auth_header.startswith(f"Bearer {ANTHROPIC_OAUTH_TOKEN_PREFIX}"):
api_key = auth_header.removeprefix("Bearer ")
for name in tuple(header_name for header_name in headers if header_name.lower() == "x-api-key"):
headers.pop(name)
headers["anthropic-beta"] = _merge_beta_headers(headers.get("anthropic-beta"), ANTHROPIC_OAUTH_BETA_HEADER)
headers["anthropic-dangerous-direct-browser-access"] = "true"
return headers, api_key
# Check api_key directly (standard chat/completion flow)
if api_key and api_key.startswith(ANTHROPIC_OAUTH_TOKEN_PREFIX):
headers.pop("x-api-key", None)
for name in tuple(header_name for header_name in headers if header_name.lower() == "x-api-key"):
headers.pop(name)
headers["authorization"] = f"Bearer {api_key}"
headers["anthropic-beta"] = _merge_beta_headers(headers.get("anthropic-beta"), ANTHROPIC_OAUTH_BETA_HEADER)
headers["anthropic-dangerous-direct-browser-access"] = "true"

View file

@ -31,13 +31,14 @@ FAKE_AUTH_TOKEN = "sk-ant-aut01-fake-auth-token-for-testing-123456789"
class TestOptionallyHandleAnthropicOAuth:
"""Tests for optionally_handle_anthropic_oauth function."""
def test_oauth_token_in_authorization_header(self):
@pytest.mark.parametrize("header_name", ["authorization", "Authorization", "AUTHORIZATION"])
def test_oauth_token_in_authorization_header(self, header_name):
"""OAuth token in Authorization header should be detected and headers set correctly."""
from litellm.llms.anthropic.common_utils import (
optionally_handle_anthropic_oauth,
)
headers = {"authorization": f"Bearer {FAKE_OAUTH_TOKEN}"}
headers = {header_name: f"Bearer {FAKE_OAUTH_TOKEN}"}
updated_headers, extracted_api_key = optionally_handle_anthropic_oauth(
headers, None
)
@ -47,6 +48,22 @@ class TestOptionallyHandleAnthropicOAuth:
assert updated_headers["anthropic-dangerous-direct-browser-access"] == "true"
assert "x-api-key" not in updated_headers
@pytest.mark.parametrize("api_key_header_name", ["x-api-key", "X-Api-Key"])
def test_oauth_removes_x_api_key_any_casing(self, api_key_header_name):
"""When OAuth wins, a client x-api-key header is removed whatever its casing."""
from litellm.llms.anthropic.common_utils import (
optionally_handle_anthropic_oauth,
)
headers = {api_key_header_name: FAKE_REGULAR_KEY, "Authorization": f"Bearer {FAKE_OAUTH_TOKEN}"}
updated_headers, extracted_api_key = optionally_handle_anthropic_oauth(
headers, None
)
assert extracted_api_key == FAKE_OAUTH_TOKEN
assert [name for name in updated_headers if name.lower() == "x-api-key"] == []
assert updated_headers["Authorization"] == f"Bearer {FAKE_OAUTH_TOKEN}"
def test_oauth_token_in_api_key_directly(self):
"""OAuth token passed as api_key should set Authorization: Bearer header."""
from litellm.llms.anthropic.common_utils import (