Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions litellm/proxy/auth/user_api_key_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,20 @@ async def _user_api_key_auth_builder( # noqa: PLR0915

if is_proxy_admin:
return UserAPIKeyAuth(
api_key=None,
user_role=LitellmUserRoles.PROXY_ADMIN,
user_id=user_id,
team_id=team_id,
team_alias=(
team_object.team_alias
if team_object is not None
else None
),
team_metadata=team_object.metadata
if team_object is not None
else None,
Comment on lines +597 to +599
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent parenthesization with team_alias

The team_metadata ternary expression lacks the explicit parentheses that team_alias (line 592-596) uses. While this works correctly due to operator precedence, the inconsistent style reduces readability within the same block.

Suggested change
team_metadata=team_object.metadata
if team_object is not None
else None,
team_metadata=(
team_object.metadata
if team_object is not None
else None
),

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

org_id=org_id,
end_user_id=end_user_id,
parent_otel_span=parent_otel_span,
)

Expand Down
74 changes: 74 additions & 0 deletions tests/test_litellm/proxy/auth/test_user_api_key_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -422,3 +422,77 @@ async def test_return_user_api_key_auth_obj_user_spend_and_budget():
assert result.user_tpm_limit == 1000
assert result.user_rpm_limit == 100
assert result.user_email == "test@example.com"


def test_proxy_admin_jwt_auth_includes_identity_fields():
"""
Test that the proxy admin early-return path in JWT auth populates
user_id, team_id, team_alias, team_metadata, org_id, and end_user_id.

Regression test: previously the is_proxy_admin branch only set user_role
and parent_otel_span, discarding all identity fields resolved from the JWT.
This caused blank Team Name and Internal User in Request Logs UI.
"""
from litellm.proxy._types import LiteLLM_TeamTable, LitellmUserRoles, UserAPIKeyAuth

team_object = LiteLLM_TeamTable(
team_id="team-123",
team_alias="my-team",
metadata={"tags": ["prod"], "env": "production"},
)

# Simulate the proxy admin early-return path (user_api_key_auth.py ~line 586)
result = UserAPIKeyAuth(
api_key=None,
user_role=LitellmUserRoles.PROXY_ADMIN,
user_id="user-abc",
team_id="team-123",
team_alias=(
team_object.team_alias if team_object is not None else None
),
team_metadata=team_object.metadata if team_object is not None else None,
org_id="org-456",
end_user_id="end-user-789",
parent_otel_span=None,
)

assert result.user_role == LitellmUserRoles.PROXY_ADMIN
assert result.user_id == "user-abc"
assert result.team_id == "team-123"
assert result.team_alias == "my-team"
assert result.team_metadata == {"tags": ["prod"], "env": "production"}
assert result.org_id == "org-456"
assert result.end_user_id == "end-user-789"
assert result.api_key is None
Comment on lines +427 to +466
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests don't exercise the actual auth code path

Both new tests construct a UserAPIKeyAuth object directly rather than invoking _user_api_key_auth_builder with a mocked JWT flow. This means they validate that UserAPIKeyAuth accepts the fields (which is trivially true since these are existing Pydantic fields), but don't verify that the production code in user_api_key_auth.py:586-603 actually passes those values correctly.

Consider adding a test that mocks the JWT auth flow (similar to test_proxy_admin_expired_key_from_cache at line 243) and asserts that the returned UserAPIKeyAuth has identity fields populated when is_proxy_admin=True.



def test_proxy_admin_jwt_auth_handles_no_team_object():
"""
Test that the proxy admin early-return path works correctly when
team_object is None (user has admin role but no team association).
"""
from litellm.proxy._types import LitellmUserRoles, UserAPIKeyAuth

team_object = None

result = UserAPIKeyAuth(
api_key=None,
user_role=LitellmUserRoles.PROXY_ADMIN,
user_id="admin-user",
team_id=None,
team_alias=(
team_object.team_alias if team_object is not None else None
),
team_metadata=team_object.metadata if team_object is not None else None,
org_id=None,
end_user_id=None,
parent_otel_span=None,
)

assert result.user_role == LitellmUserRoles.PROXY_ADMIN
assert result.user_id == "admin-user"
assert result.team_id is None
assert result.team_alias is None
assert result.team_metadata is None
assert result.org_id is None
assert result.end_user_id is None
Loading