fix(anthropic_wif): treat blank identity-source fields as unset

This commit is contained in:
mateo-berri 2026-10-02 20:48:07 -07:00
parent f4ebf1aea2
commit f4d61c3713
3 changed files with 55 additions and 3 deletions

View file

@ -175,7 +175,7 @@ def _resolve_identity_source(
legacy_ref: Final = _resolve_assertion_ref(litellm_params)
return (legacy_ref, None) if legacy_ref is not None else None
params: Final[Mapping[str, object]] = MappingProxyType(
{key: value for key, value in (litellm_params or _EMPTY_PARAMS).items() if value is not None}
{key: value for key, value in (litellm_params or _EMPTY_PARAMS).items() if _is_set(value)}
)
match source_kind:
case AnthropicIdentitySourceKind.internal_issuer.value:
@ -263,7 +263,7 @@ def _build_variant(
field_map: Mapping[str, str],
) -> _IdentitySourceVariant:
fields: Final = MappingProxyType(
{field_map[key]: value for key, value in litellm_params.items() if key in field_map}
{field_map[key]: value for key, value in litellm_params.items() if key in field_map and _is_set(value)}
)
try:
return model.model_validate(fields)
@ -512,6 +512,10 @@ def _config_value(litellm_params: Mapping[str, object] | None, param_key: str, e
return _param_str(litellm_params, param_key) or _env_str(env_name)
def _is_set(value: object) -> bool:
return value is not None and value != ""
def _param_str(litellm_params: Mapping[str, object] | None, key: str) -> str | None:
if litellm_params is None:
return None

View file

@ -25,7 +25,6 @@ from litellm.llms.base_llm.auth.identity_source import (
)
from litellm.llms.base_llm.auth.jwt_signing import build_jwks, rfc7638_thumbprint
from litellm.llms.base_llm.auth.token_exchange import JwtBearerTokenExchangeEngine
from litellm.types.router import GenericLiteLLMParams
from litellm.llms.base_llm.auth.types import (
AssertionSourceError,
ExchangeError,
@ -34,6 +33,7 @@ from litellm.llms.base_llm.auth.types import (
TokenEndpointError,
TokenTransportError,
)
from litellm.types.router import GenericLiteLLMParams
WIF_ENV_VARS: Final = (
"ANTHROPIC_FEDERATION_RULE_ID",
@ -1132,6 +1132,28 @@ class TestIdentitySourceValidationFailsClosed:
}
)
def test_blank_optional_and_foreign_fields_count_as_unset(self):
configured = {
"anthropic_federation_rule_id": "fdrl_1",
"anthropic_organization_id": "org-1",
"anthropic_identity_source": "internal_issuer",
"anthropic_issuer_url": "https://issuer.internal.example",
"anthropic_issuer_subject": "workload-a",
"anthropic_issuer_signing_key_ref": ISSUER_SIGNING_KEY_REF,
}
with_blanks = {
**configured,
"anthropic_issuer_audience": "",
"anthropic_issuer_ttl_seconds": "",
"anthropic_keycloak_client_id": "",
}
expected = resolve_anthropic_wif_params(configured)
actual = resolve_anthropic_wif_params(with_blanks)
assert expected is not None and actual is not None
assert actual.assertion_ref == expected.assertion_ref
def test_secret_pasted_into_wrong_field_never_appears_in_the_error(self):
secret_value = "super-secret-client-value-xyz"
with pytest.raises(litellm.AuthenticationError) as exc_info:

View file

@ -439,6 +439,32 @@ class TestCredentialJwksExport:
assert "JWKS_TEST_SIGNING_KEY" not in response.text
assert "PRIVATE KEY" not in response.text
def test_jwks_export_treats_blank_optional_fields_as_unset(self, restore_credential_list, monkeypatch):
monkeypatch.setenv("JWKS_TEST_SIGNING_KEY", _generate_es256_pem())
monkeypatch.setattr(
litellm,
"credential_list",
[
CredentialItem(
credential_name="anthropic-issuer-blanks",
credential_values={
"anthropic_identity_source": "internal_issuer",
"anthropic_issuer_url": "https://issuer.example.com",
"anthropic_issuer_subject": "my-workload",
"anthropic_issuer_signing_key_ref": "os.environ/JWKS_TEST_SIGNING_KEY",
"anthropic_issuer_audience": "",
"anthropic_issuer_ttl_seconds": "",
},
credential_info={"custom_llm_provider": "anthropic"},
)
],
)
response = _get_jwks("anthropic-issuer-blanks")
assert response.status_code == 200, response.text
assert response.json()["keys"][0]["kty"] == "EC"
def test_jwks_export_accepts_the_dashboard_provider_casing(self, restore_credential_list, monkeypatch):
monkeypatch.setenv("JWKS_TEST_SIGNING_KEY", _generate_es256_pem())
monkeypatch.setattr(