Skip to content

Revert "fix(auth): stop the team fallback from widening model access" (#36837) - #36982

Merged
yuneng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_/revert-36837-ui-regression
Aug 14, 2026
Merged

yuneng-berri merged 1 commit into
litellm_internal_stagingfrom
litellm_/revert-36837-ui-regression

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

TLDR

Reverts #36837 (ab2333b6c4), which locks every user out of the Admin UI.

Root cause

Every Admin UI login mints its session key against the sentinel team id
litellm-dashboard — both the password/SSO path
(litellm/proxy/auth/login_utils.py, "team_id": "litellm-dashboard") and the
EXPERIMENTAL_UI_LOGIN JWT path (ExperimentalUIJWTToken). No
LiteLLM_TeamTable row is ever created for it; it is a sentinel, and other call
sites already special-case it as "not a real team" (UI_TEAM_ID in the MCP and
agent permission handlers).

So on every UI request the team lookup asks the database for
litellm-dashboard, the database answers, and the row is not there. That is the
exact state #36837 reclassified: _get_team_object_from_user_api_key_cache
raises the new TeamNotFoundError, and _token_can_vouch_for_team refuses it
outright, with no setting able to override it. The session key also carries
models=[] and no team grant, so even the second branch would not have vouched.

The result is a 404 on every Admin-UI-authenticated request.

Proof

Both legs drive the real get_team_object with a prisma stub whose
find_unique returns None — the actual absent-row state — and feed whatever
that lookup really raises into the real _run_centralized_common_checks, with
the token shape the UI login mints. Nothing is hand-constructed or injected.

Before, at 29fe342ead (staging, with #36837)

real lookup for team='litellm-dashboard' raised: TeamNotFoundError (status 404)

  UI request /v1/models     -> HTTP 404  Team doesn't exist in db. Team=litellm-dashboard.
  UI request /user/info     -> HTTP 404  Team doesn't exist in db. Team=litellm-dashboard.
  UI request /key/list      -> HTTP 404  Team doesn't exist in db. Team=litellm-dashboard.
  UI request /team/list     -> HTTP 404  Team doesn't exist in db. Team=litellm-dashboard.
  UI request /model/info    -> HTTP 404  Team doesn't exist in db. Team=litellm-dashboard.

After, with this revert

real lookup for team='litellm-dashboard' raised: HTTPException (status 404)

  UI request /v1/models     -> SERVED
  UI request /user/info     -> SERVED
  UI request /key/list      -> SERVED
  UI request /team/list     -> SERVED
  UI request /model/info    -> SERVED

The lookup still fails in both legs — the difference is only whether the
token-derived fallback is allowed to stand in for the sentinel team.

Tests

Every test file #36837 touched, on this revert:

tests/test_litellm/proxy/auth/test_auth_checks.py
tests/test_litellm/proxy/auth/test_user_api_key_auth.py     361 passed
tests/proxy_unit_tests/test_user_api_key_auth.py             78 passed

The revert is the exact inverse of ab2333b6c4 (308 deletions, 1 insertion
against its 308/1). One conflict had to be resolved by hand: a later commit
added delete_cache_key_objects immediately below where TeamNotFoundError was
inserted. That function is unrelated and is kept.

What this reopens, stated plainly

#36837 closed a real hole: a key whose team is deleted keeps working and reaches
models the team never allowed, because team_models=[] on the token reads as
"every model". This revert reopens that. It is the right trade right now because
the dashboard is down for everyone, but it should not be left open.

The re-land needs the UI sentinel exempted before the refusal is reinstated —
team_id == UI_TEAM_ID must never reach the absent-team refusal, since it is
absent by design rather than deleted. Worth checking the same question for any
other synthetic team id in the auth path.

Type

🐛 Bug Fix

Caveats (if any)

…36837)"

This reverts commit ab2333b.

Every Admin UI login mints its session key against the sentinel team_id
`litellm-dashboard`, and no LiteLLM_TeamTable row is ever created for it.
That lookup is therefore a provably-absent row on every UI request, which
#36837 turned into a hard refusal with no override, so the whole dashboard
404s.

Reverting restores the token-derived fallback. The model-access widening
#36837 closed is reopened and needs a re-land that exempts the UI sentinel
team.
@greptile-apps

greptile-apps Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR restores Admin UI access by reverting the missing-team refusal, but it restores the fallback for every team rather than only the UI sentinel

  • Removes the distinct missing-team exception from team lookup
  • Reconstructs team authorization state from token fields after any team lookup exception
  • Removes regression coverage for missing, unreadable, and restricted teams

Confidence Score: 3/5

The PR is unsafe to merge until the fallback is restricted to the intended Admin UI sentinel

Ordinary missing teams can again be reconstructed from token defaults, and empty model grants are treated as unrestricted access

Files Needing Attention: litellm/proxy/auth/user_api_key_auth.py, litellm/proxy/auth/auth_checks.py, tests/test_litellm/proxy/auth/test_user_api_key_auth.py

Security Review

The broad authentication fallback reintroduces an authorization regression. The UI sentinel needs a scoped exception without restoring fallback behavior for ordinary teams

Important Files Changed

Filename Overview
litellm/proxy/auth/user_api_key_auth.py Restores unconditional token-derived team fallback, allowing missing ordinary teams to retain permissive authorization state
litellm/proxy/auth/auth_checks.py Removes the exception distinction between definitively absent teams and other lookup failures
tests/test_litellm/proxy/auth/test_user_api_key_auth.py Deletes regression tests that exercised missing-team refusal and restricted model grants
tests/test_litellm/proxy/auth/test_auth_checks.py Removes coverage distinguishing an absent team row from an unreadable lookup
tests/proxy_unit_tests/test_user_api_key_auth.py Removes explicit team model grants from two fixtures to match the restored permissive fallback behavior

Reviews (1): Last reviewed commit: "Revert "fix(auth): stop the team fallbac..." | Re-trigger Greptile

team_object = _team_obj_from_token(user_api_key_auth_obj)
else:
raise team_result
team_object = _team_obj_from_token(user_api_key_auth_obj) if user_api_key_auth_obj.team_id is not None else None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 security Unrestricted missing-team fallback

When an ordinary key references a missing team, token fallback treats empty team_models as unrestricted, allowing models the team did not authorize

How this was verified: The reconstructed team reaches common_checks, where an empty model list grants all-model access

Rule Used: What: Fail any PR which may contains a security in... (source)

Knowledge Base Used: Proxy Authentication and Authorization

team_object = _team_obj_from_token(user_api_key_auth_obj)
else:
raise team_result
team_object = _team_obj_from_token(user_api_key_auth_obj) if user_api_key_auth_obj.team_id is not None else None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

High: Team authorization fails open

A caller whose token references an absent or unreadable team can request models the team was not permitted to use because this fallback reconstructs the team with team_models=[], which the model check interprets as access to every model. Preserve the distinction between a missing team and a degraded database read, and handle the dashboard sentinel explicitly rather than falling back for every lookup error; other fallbacks should require a non-empty token-side grant or an explicit fail-open setting.

@veria-ai

veria-ai Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

PR overview

This PR reverts the earlier team-fallback change in authentication, restoring construction of a fallback team context from token data when a team lookup is absent or unreadable.

One high-impact authorization issue remains open. Tokens referencing a missing or unreadable team can receive unrestricted model access because an empty fallback model list is interpreted as allowing every model, enabling callers to use models their team was not permitted to access. No issues have yet been addressed.

Open issues (1)

Fixed/addressed: 0 · PR risk: 8/10

@yuneng-berri
yuneng-berri enabled auto-merge August 14, 2026 23:12
@codecov

codecov Bot commented Aug 14, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yuneng-berri
yuneng-berri merged commit 652f4cb into litellm_internal_staging Aug 14, 2026
69 of 70 checks passed
@yuneng-berri
yuneng-berri deleted the litellm_/revert-36837-ui-regression branch August 14, 2026 23:19
@codspeed

codspeed Bot commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 31 untouched benchmarks


Comparing litellm_/revert-36837-ui-regression (9592a54) with litellm_internal_staging (29fe342)1

Open in CodSpeed

Footnotes

  1. No successful run was found on litellm_internal_staging (9592a54) during the generation of this report, so 29fe342 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants