mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
fix: address issue #24436
This commit is contained in:
parent
50f88c8642
commit
147d80985f
2 changed files with 100 additions and 1 deletions
|
|
@ -1,7 +1,10 @@
|
|||
from typing import Dict, Optional
|
||||
|
||||
from litellm.llms.anthropic.common_utils import is_anthropic_oauth_key
|
||||
from litellm.types.utils import ProviderSpecificHeader
|
||||
|
||||
_ANTHROPIC_PROVIDER = "anthropic"
|
||||
|
||||
|
||||
class ProviderSpecificHeaderUtils:
|
||||
@staticmethod
|
||||
|
|
@ -14,6 +17,10 @@ class ProviderSpecificHeaderUtils:
|
|||
|
||||
Supports comma-separated provider lists for headers that work across multiple providers.
|
||||
|
||||
Anthropic OAuth tokens (sk-ant-oat*) in the Authorization header are stripped for
|
||||
non-Anthropic providers to prevent them from overriding provider-specific auth
|
||||
(e.g. AWS SigV4 for Bedrock, service account credentials for Vertex AI).
|
||||
|
||||
Returns:
|
||||
Dict: The provider specific headers for the given custom llm provider
|
||||
"""
|
||||
|
|
@ -24,6 +31,16 @@ class ProviderSpecificHeaderUtils:
|
|||
provider_list = [p.strip() for p in stored_providers.split(",")]
|
||||
|
||||
if custom_llm_provider in provider_list:
|
||||
return provider_specific_header.get("extra_headers", {})
|
||||
headers = provider_specific_header.get("extra_headers", {})
|
||||
# Anthropic OAuth tokens must not be forwarded to non-Anthropic providers.
|
||||
# Forwarding them would overwrite provider-specific auth headers
|
||||
# (e.g. Bedrock's SigV4 Authorization, Vertex AI service-account auth).
|
||||
if custom_llm_provider != _ANTHROPIC_PROVIDER:
|
||||
headers = {
|
||||
k: v
|
||||
for k, v in headers.items()
|
||||
if not (k.lower() == "authorization" and is_anthropic_oauth_key(v))
|
||||
}
|
||||
return headers
|
||||
|
||||
return {}
|
||||
|
|
|
|||
|
|
@ -112,3 +112,85 @@ class TestProviderSpecificHeaderUtils:
|
|||
provider_specific_header, None
|
||||
)
|
||||
assert result == {}
|
||||
|
||||
def test_anthropic_oauth_token_not_forwarded_to_bedrock(self):
|
||||
"""
|
||||
Regression test for https://github.com/BerriAI/litellm/issues/24436.
|
||||
|
||||
When Claude Code sets an Anthropic OAuth token (sk-ant-oat*) in Authorization,
|
||||
it must NOT reach Bedrock — forwarding it overwrites the AWS SigV4 Authorization
|
||||
header and causes a 403 from Bedrock.
|
||||
"""
|
||||
oauth_token = "Bearer sk-ant-oat01-abc123xyz"
|
||||
provider_specific_header: ProviderSpecificHeader = {
|
||||
"custom_llm_provider": "anthropic,bedrock,vertex_ai",
|
||||
"extra_headers": {
|
||||
"Authorization": oauth_token,
|
||||
"anthropic-beta": "some-beta-feature",
|
||||
},
|
||||
}
|
||||
|
||||
# Anthropic should receive the OAuth token
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "anthropic"
|
||||
)
|
||||
assert result["Authorization"] == oauth_token
|
||||
assert result["anthropic-beta"] == "some-beta-feature"
|
||||
|
||||
# Bedrock must NOT receive the OAuth token (it would overwrite SigV4 auth)
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "bedrock"
|
||||
)
|
||||
assert "Authorization" not in result
|
||||
assert result.get("anthropic-beta") == "some-beta-feature"
|
||||
|
||||
# Vertex AI must NOT receive the OAuth token
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "vertex_ai"
|
||||
)
|
||||
assert "Authorization" not in result
|
||||
assert result.get("anthropic-beta") == "some-beta-feature"
|
||||
|
||||
def test_anthropic_oauth_token_not_forwarded_to_bedrock_converse(self):
|
||||
"""Anthropic OAuth token must not be forwarded to bedrock_converse either."""
|
||||
oauth_token = "Bearer sk-ant-oat02-xyz789"
|
||||
provider_specific_header: ProviderSpecificHeader = {
|
||||
"custom_llm_provider": "anthropic,bedrock,bedrock_converse,vertex_ai",
|
||||
"extra_headers": {"Authorization": oauth_token},
|
||||
}
|
||||
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "bedrock_converse"
|
||||
)
|
||||
assert result == {}
|
||||
|
||||
def test_non_oauth_authorization_still_forwarded_to_non_anthropic(self):
|
||||
"""A plain Bearer token (not Anthropic OAuth) should still pass through."""
|
||||
plain_token = "Bearer some-regular-api-key"
|
||||
provider_specific_header: ProviderSpecificHeader = {
|
||||
"custom_llm_provider": "openai,azure",
|
||||
"extra_headers": {"Authorization": plain_token},
|
||||
}
|
||||
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "openai"
|
||||
)
|
||||
assert result["Authorization"] == plain_token
|
||||
|
||||
def test_anthropic_oauth_token_raw_format_not_forwarded_to_bedrock(self):
|
||||
"""Anthropic OAuth token in raw format (without 'Bearer ' prefix) is also stripped."""
|
||||
raw_oauth_token = "sk-ant-oat01-abc123"
|
||||
provider_specific_header: ProviderSpecificHeader = {
|
||||
"custom_llm_provider": "anthropic,bedrock,vertex_ai",
|
||||
"extra_headers": {"Authorization": raw_oauth_token},
|
||||
}
|
||||
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "bedrock"
|
||||
)
|
||||
assert "Authorization" not in result
|
||||
|
||||
result = ProviderSpecificHeaderUtils.get_provider_specific_headers(
|
||||
provider_specific_header, "anthropic"
|
||||
)
|
||||
assert result["Authorization"] == raw_oauth_token
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue