mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
42 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b76def0e5d
|
test: require a match= on broad pytest.raises, and drop duplicate parametrize cases (#37769)
`pytest.raises(Exception)` with no `match=` passes on any error that broad. A TypeError from a refactor, a botched fixture, an import that moved: all of them read as the rejection the test claims to police, so the test goes green for the wrong reason and stays green after the behaviour it guards is gone. PT011 closes that gap for the 317 sites B017 could not reach, because B017 only fires on a single-statement body with no `as e` binding. Each pattern here is the message the code actually raised, recorded by running the sites under a plugin that logged the concrete type and text per call site, so the assertions describe observed behaviour rather than a guess. Where a site raises more than one message across its parametrize cases, the pattern is an alternation of what was seen; where the exception carries an empty `str()` and puts the text on `.message`, the site keeps a narrow `noqa` with the reason. PT014 removes four parametrize cases that were listed twice. The duplicate re-runs an assertion that already passed, and it usually marks a case someone meant to vary and forgot to edit. |
||
|
|
52403d7a8d
|
fix(jwt): retry JWKS fetches, serve stale keys, and return 503 when the IdP is unreachable (#37690)
A JWKS fetch had no retry, so a single connect timeout to the identity provider failed authentication outright, and once the cached copy expired there was nothing to fall back on. How that surfaced depended on the outage shape: httpx.ConnectTimeout was missing from DB_CONNECTION_ERROR_TYPES so it fell through to the generic auth handler as a 401 with an empty detail, while a read timeout took the database path and reported a healthy database as unreachable. Transport failures are now retried three times with a short backoff, and the last-known-good JWKS stays usable for a bounded window past public_key_ttl. That window is public_key_stale_ttl, a new config field defaulting to 3600s and settable to 0 to fail closed. It is checked on every read against the current setting rather than baked into the cache entry when it is written, so lowering it binds immediately instead of waiting for entries written under the old value to age out, which matters because a shared cache survives the restart an operator performs to make the change take effect. A copy whose write time cannot be established is not servable. Only httpx.TransportError unlocks the stale copy, so an identity provider that answers at all, including with a narrowed key set, revokes on the next refresh. Every stale serve logs the kid it authenticated, how long ago that copy was refreshed, and how long until it stops being trusted. A sustained outage is remembered for 30s per key url, so it costs one fetch per window instead of three timeouts per request serialised behind the refresh lock. Non-200 JWKS responses now raise instead of being cached as the key set, which previously let an error body overwrite the last-known-good copy. An unreachable identity provider with no cached copy left returns 503 auth_provider_unavailable. Resolves LIT-5524 Co-authored-by: Yassin Kortam <yassin@berri.ai> |
||
|
|
c2f0014a63 | fix(jwt_auth): grant only /v1/messages routes to JWT teams by default, not all anthropic_routes | ||
|
|
aca7f57324 |
fix(jwt_auth): allow /v1/messages for JWT teams by default
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> |
||
|
|
e17f3b6e1a
|
fix(proxy): populate user_email on UserAPIKeyAuth for JWT auth (#34174)
JWT auth built UserAPIKeyAuth without user_email even though the resolved user row and the JWT email claim were both available, so the user_email label on Prometheus metrics and user_api_key_user_email in StandardLogging/SpendLogs metadata were always None for JWT traffic. Plumb user_email through JWTAuthBuilderResult: auth_builder returns the user row email when set, falling back to the user_email_jwt_field claim (covers the scope-based proxy-admin path where no user row is loaded). The JWT branch now stamps it on the proxy-admin return, the standard valid_token, and the auto-registered virtual key object. Resolves LIT-4238 |
||
|
|
0855fa02b2
|
feat(jwt): fall back to DB team memberships when JWT has no team claims (#31356)
* feat(jwt): fall back to DB team memberships when JWT has no team claims
* style(jwt): use PEP 585/604 annotations in DB team fallback to clear strict gate
* fix(jwt): preserve DB teams on no-claim sync, model-gate DB fallback, stop team-id leak
When fallback_to_db_teams is enabled and a JWT carries no team claims,
sync_user_role_and_teams previously computed teams_to_remove as every existing
DB membership and wiped the user out of all their teams on each request, which
also left the DB fallback nothing to resolve. Skip team removal in that case so
memberships survive and the fallback can attribute usage.
Apply the same per-team model-access check the claim-based path enforces when
selecting a DB fallback team, so a team's models restriction is no longer
bypassed; a team that cannot serve the requested model is skipped in favor of
one that can.
Drop the user's team-id list from the x-litellm-team-id membership 403 detail so
a valid-JWT caller can no longer enumerate team IDs.
* fix(jwt): load team membership on DB fallback; scope header check to provisional teams
The DB-team fallback resolved a team but never loaded its team membership
row, so per-team membership budget limits were silently skipped on that
path. _resolve_db_team_fallback now fetches the resolved team's membership
when a user_id is known and returns it, matching the claim-based path so
downstream LiteLLM_TeamMembership budget enforcement works there too.
The provisional x-litellm-team-id validation also fired on any non-None
team_id, including an RBAC role-derived one, which 403'd RBAC team flows
when the asserted team was not also a DB membership. It now runs only when
team_id actually came from the header (team_id == header_team_id).
* fix(jwt): surface DB-fallback membership lookup failures at warning level
A transient get_team_membership failure on the DB team fallback path is
recoverable: the team is still resolved and the request proceeds, just
without per-team membership budget enforcement for that request. Logging
that at debug hid a silent budget-enforcement gap from operators, so it now
logs at warning and states that enforcement was skipped. Behavior is
otherwise unchanged: the resolved team is returned with a None membership
rather than failing the request, covered by
test_resolve_db_team_fallback_survives_membership_lookup_error.
* fix(jwt-auth): tighten db-team fallback gating and passthrough enforcement
Resolves four issues in the fallback_to_db_teams path:
- _resolve_db_team_fallback now surfaces a model-access denial when memberships
exist but none can access the requested model, instead of always returning
the no-membership message
- auth_builder gates the fallback on real JWT team claims via
get_all_jwt_team_ids so a configured team_id_default does not silently route
claimless tokens to the default team
- A team selected only via _resolve_db_team_fallback is re-validated against
the team's allowed_passthrough_routes; the earlier gate ran while team_id
was still None
- sync_user_role_and_teams considers both plural and singular team claim
shapes when reconciling DB memberships so singular-only tokens
(Okta/Auth0 defaults) no longer leave stale teams behind
* fix(jwt): don't upsert a provisional x-litellm-team-id before membership check
When fallback_to_db_teams is on and the JWT carries no team claims, an
x-litellm-team-id header is accepted provisionally and only validated against
the user's DB memberships later in auth_builder. With team_id_upsert also
enabled, get_team_object ran the upsert on that unvalidated header team first,
so an attacker-supplied header could create an orphaned team row before the
403 membership check. Suppress the upsert whenever the team is provisional
(db_team_fallback), since a genuine membership team already exists and an
invalid one must not be created. Regression:
test_auth_builder_provisional_header_team_is_not_upserted.
* fix(jwt): pin RBAC-asserted team against db-team-fallback header override
When a JWT carries an RBAC team role but no group claims, auth_builder already
sets team_id from the RBAC object_id. db_team_fallback still evaluated true
there, so the provisional x-litellm-team-id path accepted a header team and
silently overrode the RBAC-asserted team with any team the caller belonged to.
Gate db_team_fallback on team_id being unset, and drive the header's provisional
acceptance off db_team_fallback rather than the raw flag, so an RBAC token plus
a non-claim header team is rejected with 403 instead of substituting the team.
Regression: test_auth_builder_header_cannot_override_rbac_team_under_db_fallback.
* fix(jwt): scope dual-claim membership sync to fallback_to_db_teams
The membership sync read both plural and singular JWT team claims via
get_all_jwt_team_ids unconditionally, which silently changed reconciliation
for every deployment using sync_user_role_and_teams, not just those opting
into fallback_to_db_teams: a singular-only IdP token that previously stripped
all DB teams would now be recognized. Gate the dual-claim read on
fallback_to_db_teams so flag-off deployments keep the upstream plural-only
behavior, honoring the PR's contract that existing deployments are unchanged.
Regression: test_sync_user_role_and_teams_singular_claim_only_recognized_under_flag.
* fix(jwt): drop user team IDs from db-fallback model-access 403 detail
The model-access-denied 403 in _resolve_db_team_fallback echoed the user's
full DB team-id list in its detail. It is only the caller's own memberships,
but it is inconsistent with the membership-validation 403 in the same feature
that was deliberately scrubbed of team IDs. Replace the enumerated list with a
generic "no team you are a member of has access" message. Regression extends
test_resolve_db_team_fallback_distinguishes_no_membership_vs_model_denied to
assert the team id is absent from the detail.
* fix(jwt): keep db-team fallback off for alias-only tokens
* test(jwt): cover alias-only token skipping db-team fallback
The autofix in
|
||
|
|
84266bf924
|
feat(auth): resolve caller identity once into a Principal at the auth seam (#30887)
Introduce a single, typed caller identity that is resolved once at the auth boundary and read by reference downstream, instead of being re-derived from a 50-field key object or rebuilt from request metadata. What this adds (litellm/proxy/auth/resolvers/), organized by responsibility: - Principal: a small, frozen, identity-only value type (user / organization / teams / project / end-user / roles / scopes / network), with its sub-models and the role mapping. No budget or policy state; those stay on the key object. - DbIdentityStore: the auth flow's resolver, owning both halves of resolving a caller. resolve_key does the one combined_view lookup (cache, then DB via the shared lower-level helpers, then write-back) and returns the key object, which still flows for budget / rate-limit / policy unchanged. principal_from_key projects the identity slice of that key object into a Principal, issuing no lookup. user_api_key_auth resolves every key through the store rather than calling get_key_object directly; auth_checks.get_key_object stays as the legacy entrypoint for its other callers until they migrate. - network: the X-Forwarded-For / trusted-proxy CIDR primitives live here in one place. trusted_proxy_utils now imports them rather than keeping a second copy. At the seam, user_api_key_auth projects one per-request Principal off the resolved key object and stamps the request network context onto it once (X-Forwarded-For is trusted only when trusted_proxy_ranges is configured). It is attached to request.state.principal for the downstream consumers later phases add. The projection is additive and defensive: a failure never rejects an already-authenticated request, and a missing principal must be treated as deny by any future reader. The Principal is always identifiable (credential_ref and a stable subject off the token), never anonymous. This is additive and changes no behavior today; it is the identity foundation the spend-attribution and authorization phases build on. |
||
|
|
a7ecf6b5b1
|
feat(jwt-auth): opt-in fallback to DB team on unresolved JWT claim (#28913)
* fix(jwt-auth): defer to single-team DB fallback on claim mismatch Extends the single-team DB fallback introduced in #26418 to two more cases where it previously could not run: * `find_and_validate_specific_team_id`: when `team_id_jwt_field` is configured and a claim value is present in the token but the team does not exist in the LiteLLM DB (HTTPException 404 from `get_team_object`), return `(None, None)` instead of raising — the auth_builder fallback then attributes the request to the user's single DB team. Only HTTPException is caught; other errors (e.g. "No DB Connected") still propagate. * `find_team_with_model_access`: when none of the `team_ids_jwt_field` groups resolve to a real LiteLLM team, return `(None, None)` instead of raising 403 so the same fallback path runs. If at least one group DID resolve to a team but none granted the requested model, the original 403 is preserved (legitimate access denial — not a claim mismatch). Tracked via the new `any_claim_team_resolved` flag. The strict `is_required_team_id` raise and `enforce_team_based_model_access` raise remain unchanged. Unit tests cover both new soft-fail paths and guard each preserved path (strict required, enforce_team_based, the preserved 403, and the non-HTTPException propagation). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(jwt-auth): narrow HTTPException catch to 404 (greptile review) Address Greptile review comments on #28913: * `find_and_validate_specific_team_id`: re-raise HTTPException when `status_code != 404`, pinning the catch to the "team doesn't exist in db" path documented for `get_team_object`. A future change that introduces a different status code (e.g. 403 for a blocked team) will now propagate instead of silently falling through to the single-team DB fallback. * Add `test_find_and_validate_specific_team_id_non_404_http_exception_propagates` parametrised over 400 / 403 / 500 to lock in the contract. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(jwt-auth): gate claim-mismatch fallback behind opt-in flag The unresolved-team-claim fallback added in the previous commit weakened the strict claim-based authorization contract by default — an authenticated user whose JWT carries a stale or invalid team claim could still consume their single DB team's models/quota via the fallback. Gate both soft-fail paths in `find_and_validate_specific_team_id` and `find_team_with_model_access` behind a new opt-in flag `team_claim_fallback` on `LiteLLM_JWTAuth` (default False). Default-off preserves the pre-existing strict behavior. Operators who intentionally treat IdP team claims as advisory (e.g. machine tokens whose group claims live in a separate namespace from LiteLLM team_ids) opt in via config. Adds two regression tests guarding the default-off behavior. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
b7f47a3b52
|
fix(jwt): use resolved DB user_id for spend on legacy email match (#29217)
* fix(jwt): attribute spend to resolved DB user_id on email/sso fuzzy match When user_id_upsert is enabled with JWT auth and a pre-migration user row exists whose user_email matches the JWT email but whose user_id is a UUID, get_user_object resolves the legacy row via fuzzy lookup, but the JWT-claim user_id (the email) still flowed into team-membership lookup, JWTAuthBuilderResult.user_id, UserAPIKeyAuth and the spend tables. Spend was orphaned under a phantom email id; /user/info and the Usage page showed $0 for the legacy user (GH #26789). Treat the resolved user_object as the source of truth: add _canonical_user_id_from_db, rebind inside get_objects, and return effective_user_id so auth_builder unpacks it without adding statements. Fixes #26789 Co-authored-by: Cursor <cursoragent@cursor.com> * fix(jwt): log user_id rebind at DEBUG to avoid email PII in INFO streams Greptile review on #29217: rebinding often logs JWT email claims at INFO. Co-authored-by: Cursor <cursoragent@cursor.com> * test(jwt): update passthrough allowlist mock for 5-tuple get_objects Staging #29256 added a test that still mocked get_objects with a 4-tuple; our PR expanded the return to 5 values (effective_user_id). Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
20dc6dffa4
|
fix(proxy): passthrough 404 when SERVER_ROOT_PATH is set (#29658)
* fix(proxy): match passthrough registry routes bare-to-bare with SERVER_ROOT_PATH After #28547, get_request_route strips the deployment prefix while registry lookup still re-inflated stored paths via SERVER_ROOT_PATH, causing 404s under paths like /llmproxy/ml. Compare normalized bare routes in both is_registered_pass_through_route and get_registered_pass_through_route. Co-authored-by: Cursor <cursoragent@cursor.com> * test(proxy): patch utils.get_server_root_path in passthrough auth tests After removing get_server_root_path from pass_through_endpoints, route and JWT tests must mock litellm.proxy.utils where normalization reads it. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9d9558e78f
|
fix(auth): preserve 401 status for expired JWTs in OTel traces (#29510)
* fix(auth): preserve 401 status for expired JWTs in OTel traces Expired JWT access tokens raised a generic Exception with no status code attached. Because the codeless exception was logged to OTel via post_call_failure_hook before auth_exception_handler re-wrapped it as ProxyException(401), the OTel span never set http.response.status_code and trace viewers displayed it as a generic 500. Clients still got a 401 back, so traces and actual responses diverged. Raise ProxyException(code=401, type=expired_key) directly at the source in both JWT decode paths so the 401 is consistent across the client response and the OTel http.response.status_code attribute, matching how virtual-key expirations are handled. * fix(auth): preserve 401 for expired JWTs on issuer-scoped path The issuer-scoped JWT path (_auth_jwt_with_issuer) still raised a generic Exception on expiry, surfacing as a 500 in client responses and OTel traces. Raise ProxyException with expired_key/401 there too, matching auth_jwt, and add a regression test exercising the issuer path end-to-end |
||
|
|
6d6eda8101
|
[internal copy of #28008] Support MCP OAuth passthrough and issuer-scoped JWT auth (#28356)
* fix(proxy): point /metrics 401 at the opt-out flag Operators upgrading past |
||
|
|
dc4f5b12ef
|
fix(proxy): enforce allowed_passthrough_routes for auth=true pass-thr… (#29256)
* fix(proxy): enforce allowed_passthrough_routes for auth=true pass-through
Pass-through endpoints with auth=true were injected into openai_routes,
so teams with openai_routes access bypassed per-team allowed_passthrough_routes.
Gate auth-enforced pass-through at JWT, virtual-key, and non-admin route checks.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(proxy): clarify JWT passthrough denial
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(proxy): make pass-through auth checks method-aware
Prevent allowlist bypass when the same path is registered with different auth settings per HTTP method.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix passthrough route auth checks
* fix(proxy): reject unregistered pass-through HTTP methods
Enforce method-aware JWT checks and return 405 when stale FastAPI routes accept requests outside the current pass-through registry.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(proxy): remove duplicate request_method in JWT team lookup
Fixes SyntaxError on proxy startup caused by passing request_method twice to find_team_with_model_access.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix passthrough route auth enforcement
* fix(proxy): raise passthrough-specific 403 directly in virtual-key path
* fix(proxy): load team for RBAC role-claim JWT passthrough gating
* Revert "chore(tests): migrate Bedrock CI to AWS account 941277531214 (#28728)" (#29326)
This reverts the Bedrock CI account migration (#28728). The original account
(888602223428) was put under an AWS security restriction after a leaked key
and has since been reactivated, while the replacement account (941277531214)
lacks access to several models the suites exercise (legacy Bedrock Claude 3
models, Cohere, Nova Canvas image gen, Bedrock batch inference, and flagship
Opus). Pointing CI back at the reactivated account restores that coverage.
This is the exact inverse of #28728: all hardcoded 941277531214 references go
back to 888602223428 (provisioned/imported-model ARNs, AgentCore runtime ARNs
and their suffixes, batch execution role ARN, and the example proxy config),
the S3 buckets revert to litellm-proxy and load-testing-oct, the guardrail IDs
revert to wf0hkdb5x07f and ff6ujrregl1q, the SageMaker endpoint and Knowledge
Base revert to their original ids, and the live-call tests go back to the
legacy model strings. The grid_spec fail_reason workaround for the unentitled
Opus cells is dropped while keeping the unrelated bedrock_effort_ceiling field
added after the migration.
The CircleCI AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars still point at
941277531214 and must be set to the reactivated account's fresh credentials
separately via the CircleCI API; AWS_REGION_NAME stays us-west-2.
(cherry picked from commit
|
||
|
|
055bdc3507
|
fix(auth): harden JWT routing wildcard iss and merge list team_id claims
Reject fnmatch wildcards on non-scope claims when the claim string contains whitespace so malformed iss values cannot match patterns like trusted.*. Merge every entry when team_id_jwt_field resolves to a list instead of keeping only the first element. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
158b0c28c0
|
[litellm-agent] Staging → litellm_internal_staging (5/7/2026) (#27375)
Squash-merged by litellm-agent from oss-pr-review-agent-shin[bot]'s PR. |
||
|
|
c3f7158b2b
|
Merge pull request #27008 from stuxf/fix/jwt-audience-and-issuer-verification
Some checks are pending
Unit Tests: Proxy DB Operations / proxy-runtime (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / endpoints-and-responses (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / guardrails-hooks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / jwt-and-keys (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / key-generation (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / logging-misc (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-server-core (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / schema-migration (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / proxy-utils (push) Blocked by required conditions
Unit Tests: Security / security (push) Waiting to run
Unit Tests: Caching (Redis) / caching-redis (push) Waiting to run
Unit Tests: Proxy DB Operations / assert-shard-coverage (push) Waiting to run
Unit Tests: Proxy DB Operations / auth-checks (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / budgets (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / custom-logging (push) Blocked by required conditions
Unit Tests: Proxy DB Operations / db-and-spend (push) Blocked by required conditions
fix(auth): support JWT issuer verification + warn when unscoped |
||
|
|
e55401e39c
|
fix(auth): support JWT issuer verification, scope-warning when unscoped
When JWT auth is enabled but `JWT_AUDIENCE` is unset, `auth_jwt` disabled audience verification entirely. Tokens minted by any other application that shared the same IdP signing keys (Azure AD, Okta, etc.) were accepted as long as their signature checked out, even though their `aud` and `iss` claims pointed at unrelated apps. The proxy then fell into the no-team / no-user branch where access checks default-allow. This change: 1. Adds support for the `JWT_ISSUER` env var. When set, PyJWT verifies the token's `iss` claim — turning on the same defense for tokens that share an audience but come from a different IdP tenant. 2. Refactors the duplicated `jwt.decode` calls (RSA/EC/OKP path and x509 path) into a single `_build_decode_kwargs` helper that computes audience, issuer, and the corresponding `verify_*` opt-outs once per call. 3. Logs a single startup-time warning when JWT auth is enabled but neither `JWT_AUDIENCE` nor `JWT_ISSUER` is configured, so operators running the insecure default see a flag in their logs without getting spammed per-request. Default behavior (no env vars) is preserved for backward compatibility. Setting `JWT_AUDIENCE` and/or `JWT_ISSUER` opts into the verification. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1d62ca0e23
|
Merge branch 'litellm_internal_staging' into litellm_token_verification_query_opt | ||
|
|
18996326ef | update test cases to fix handle_jwt test cases | ||
|
|
3c0d172d4e
|
fix(proxy): single-team DB fallback when JWT has no team_id (#26418)
* fix(proxy): infer team from DB when JWT has no team and user has one team - When team_id is unset after JWT auth but the user row has exactly one team, set team_id, team_object, and team_membership from DB. - Skip when zero or multiple teams (ambiguous). - Add parametrized unit tests in test_handle_jwt.py. Made-with: Cursor * fix(proxy): JWT single-team DB fallback: catch errors, tests match get_team_object - Wrap get_team_object + get_team_membership in one try/except; log and skip on failure (stale/missing team id no longer fails auth). - Parametrize tests: HTTP 404/500, membership error; use side_effect not return_value=None for missing team row. Made-with: Cursor * refactor(jwt): extract single-team fallback into _resolve_single_team_fallback helper Made-with: Cursor |
||
|
|
e1bb542556
|
chore: fix linting (ruff PLR0915, black) on admin team-header fix
Extract the admin team-header attachment into a helper so auth_builder stays under the 50-statement lint threshold; apply black formatting to the two files flagged on the prior commit. No behavior change. |
||
|
|
6ea95a6379
|
fix(jwt-auth): apply team TPM/RPM + attribution for admins using x-litellm-team-id
Scope the header-driven team fetch to LLM API routes so admin management routes keep the pre-existing bypass behavior (no phantom teams, no 404s on mgmt calls). Team context is threaded onto UserAPIKeyAuth so spend logs, rate limits, and team_models attribution are correctly applied when admins act on behalf of a team via x-litellm-team-id. |
||
|
|
e8461b5b97
|
style: run black formatter on files from main merge | ||
|
|
4c00a14ce0 | fix: fix ci/cd + handle oidc jwt tokens | ||
|
|
dd11e77852
|
fix: add explicit TTL to cache writes and test coverage for user cache invalidation
Add DEFAULT_MANAGEMENT_OBJECT_IN_MEMORY_CACHE_TTL to both async_set_cache calls in sync_user_role_and_teams for consistency with all other user cache writes. Add 3 tests covering cache invalidation on role change, team change, and no-op when nothing changes. |
||
|
|
a07d041881 |
fix: apply same AsyncMock pattern to remaining OIDC discovery test
Address Greptile review: test_resolve_jwks_url_resolves_oidc_discovery_document also used the inconsistent patch.object pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
eb658693a3 |
fix: use direct AsyncMock assignment instead of patch.object in JWT tests
The patch.object with new_callable=AsyncMock can behave inconsistently across Python versions, causing mock_response.status_code to return a MagicMock instead of the assigned value. Direct assignment is simpler and more reliable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
6bcba46dda
|
fix: set mock status_code in JWT OIDC discovery tests (#22361)
The _resolve_jwks_url method checks response.status_code != 200, but MagicMock returns a MagicMock object for status_code which is always truthy (!= 200). Explicitly set mock_response.status_code = 200 so the tests exercise the intended code path. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
5f28422f49 |
fix(types): filter null fields from reasoning output items (#22370)
* fix(image_generation): propagate extra_headers to OpenAI image generation Add headers parameter to image_generation() and aimage_generation() methods in OpenAI provider, and pass headers from images/main.py to ensure custom headers like cf-aig-authorization are properly forwarded to the OpenAI API. Aligns behavior with completion() method and Azure provider implementation. * test(image_generation): add tests for extra_headers propagation Verify that extra_headers are correctly forwarded to OpenAI's images.generate() in both sync and async paths, and that they are absent when not provided. * Add Prometheus child_exit cleanup for gunicorn workers When a gunicorn worker exits (e.g. from max_requests recycling), its per-process prometheus .db files remain on disk. For gauges using livesum/liveall mode, this means the dead worker's last-known values persist as if the process were still alive. Wire gunicorn's child_exit hook to call mark_process_dead() so live-tracking gauges accurately reflect only running workers. * docs: update AssemblyAI docs with Universal-3 Pro, Speech Understanding, and LLM Gateway (#21130) * docs: update AssemblyAI docs with Universal-3 Pro, Speech Understanding, and LLM Gateway provider config * feat: add AssemblyAI LLM Gateway as OpenAI-compatible provider * fix(mcp): update test mocks to use renamed filter_server_ids_by_ip_with_info Tests were mocking the old method name `filter_server_ids_by_ip` but production code at server.py:774 calls `filter_server_ids_by_ip_with_info` which returns a (server_ids, blocked_count) tuple. The unmocked method on AsyncMock returned a coroutine, causing "cannot unpack non-iterable coroutine object" errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(test): update realtime guardrail test assertions for voice violation behavior Tests were asserting no response.create/conversation.item.create sent to backend when guardrail blocks, but the implementation intentionally sends these to have the LLM voice the guardrail violation message to the user. Updated assertions to verify the correct guardrail flow: - response.cancel is sent to stop any in-progress response - conversation.item.create with violation message is injected - response.create is sent to voice the violation - original blocked content is NOT forwarded Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(bedrock): restore parallel_tool_calls mapping in map_openai_params The revert in |
||
|
|
3ff70598ad
|
fix: bump litellm-proxy-extras to 0.4.50 and fix 3 failing tests (#22417)
* fix(ci): handle inline table in pyproject.toml for litellm-proxy-extras version check * fix: bump litellm-proxy-extras to 0.4.50 in pyproject.toml, requirements.txt, and poetry.lock * fix(tests): set status_code=200 on JWT mocks and pass pii_tokens through data in presidio test |
||
|
|
ee703cea99
|
fix(jwt): OIDC discovery URLs, roles array handling, dot-notation error hints (#22336)
* fix(jwt): support OIDC discovery URLs, handle roles array, improve error hints Three fixes for Azure AD JWT auth: 1. OIDC discovery URL support - JWT_PUBLIC_KEY_URL can now be set to .well-known/openid-configuration endpoints. The proxy fetches the discovery doc, extracts jwks_uri, and caches it. 2. Handle roles claim as array - when team_id_jwt_field points to a list (e.g. AAD's "roles": ["team1"]), auto-unwrap the first element instead of crashing with 'unhashable type: list'. 3. Better error hint for dot-notation indexing - when team_id_jwt_field is set to "roles.0" or "roles[0]", the 401 error now explains to use "roles" instead and that LiteLLM auto-unwraps lists. * Add integration demo script for JWT auth fixes (OIDC discovery, array roles, dot-notation hints) Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> * Add demo_servers.py for manual JWT auth testing with mock JWKS/OIDC endpoints Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> * Add demo screenshots for PR comment Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> * Add integration test results with screenshots for PR review Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> * address greptile review feedback (greploop iteration 1) - fix: add HTTP status code check in _resolve_jwks_url before parsing JSON - fix: remove misleading bracket-notation hint from debug log (get_nested_value does not support it) * Update tests/test_litellm/proxy/auth/test_handle_jwt.py Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * remove demo scripts and assets --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> |
||
|
|
edc8413f1e
|
Add Kubernetes ServiceAccount JWT authentication support (#18055)
* Allow get_nested_value dot notation to support escaping for Kubernetes JWT Support * Add support for team and org alias fields, add docs, tests * Fix lint issue with max statements in handle jwt logic |
||
|
|
d38f241032
|
[Feat] JWT Auth - auth allow selecting team_id from request header (#17884)
* feat: add get_team_id_from_header for JWT Auth * fix Auth builder JWT Auth * test_get_team_id_from_header * test_auth_builder_uses_team_from_header_e2e * Select Team via Request Header |
||
|
|
24f847b84c
|
[Feat] JWT Auth - AI Gateway, allow using regular OIDC flow with user info endpoints (#17324)
* feat: allow fetching OIDC user info * test: use test_auth_builder_with_oidc_userinfo_enabled gets user info when enabled * fix tool permission doc * docs fix diagram |
||
|
|
086621e3d3 | test_handle_jwt.py | ||
|
|
8e76f8e7d0
|
[Feat] Team Member Rate Limits + Support for using with JWT Auth (#13601)
* fix - assign tpm/rpm limit onJWT * add team member rpm/tpm limits * update - rate limiter v3 with team member rate limits * update utils * fixes for LiteLLM_BudgetTable * undo change * add TeamMemberBudgetHandler * add _process_team_member_budget_data * add get_team_membership * add safe_get_team_member_rpm_limit and safe_get_team_member_tpm_limit * LiteLLM_TeamMembership * add LiteLLM_TeamMembership rate limit for JWTs * fix * tests |
||
|
|
f60a9cf908
|
[Bug]: Fix JWTs access not working with model groups (#13474)
* fix can_team_access_model * test_find_team_with_model_access_model_group |
||
|
|
8826e02a98
|
feat: Add dot notation support for all JWT fields (#13013)
* feat: Add dot notation support for all JWT fields - Updated all JWT field access methods to use get_nested_value for dot notation support - Enhanced get_team_id to properly handle team_id_default fallback with nested fields - Added comprehensive unit tests for nested JWT field access and edge cases - Updated documentation to reflect dot notation support across all JWT fields - Maintains full backward compatibility with existing flat field configurations Supported fields with dot notation: - team_id_jwt_field, team_ids_jwt_field, user_id_jwt_field - user_email_jwt_field, org_id_jwt_field, object_id_jwt_field - end_user_id_jwt_field (roles_jwt_field was already supported) Example: user_id_jwt_field: 'user.sub' accesses token['user']['sub'] * fix: Add type annotations to resolve mypy errors - Add explicit type annotation for team_ids variable in get_team_ids_from_jwt - Add type ignore comment for sentinel object return in get_team_id - Resolves mypy errors while maintaining functionality * fix: Resolve mypy type error in get_team_ids_from_jwt - Remove explicit List[str] type annotation that conflicts with get_nested_value return type - Simplify return logic to use 'team_ids or []' ensuring always returns List[str] - Fixes: Incompatible types in assignment (expression has type 'list[str] | None', variable has type 'list[str]') * fix: Add proper type annotation for team_ids variable - Use Optional[List[str]] type annotation to satisfy mypy requirements - Resolves: Need type annotation for 'team_ids' [var-annotated] - Maintains functionality while ensuring type safety * refactor: remove outdated JWT unit tests and consolidate JWT-related functionality - Deleted the test_jwt.py file as it contained outdated and redundant tests. - Consolidated JWT-related tests into test_handle_jwt.py for better organization and maintainability. - Updated tests to ensure proper functionality of JWT handling, including token validation and role mapping. - Enhanced test coverage for JWT field access and nested claims handling. * test: add comprehensive unit tests for JWT authentication - Introduced a new test file `test_jwt.py` containing unit tests for JWT authentication. - Implemented tests for loading configuration with custom role names, validating tokens, and handling team tokens. - Enhanced coverage for JWT field access, nested claims, and role-based access control. - Added fixtures for Prisma client and public JWT key generation to support testing. - Ensured proper handling of valid and invalid tokens, including user and team scenarios. * revert test_handle_jwt.py * rename file * test: remove outdated JWT nesting tests and add new nested field access tests - Deleted the `test_jwt_nesting.py` file as it contained outdated tests. - Introduced new tests in `test_handle_jwt.py` to verify nested JWT field access. - Enhanced coverage for accessing nested values using dot notation and ensured backward compatibility with flat field names. - Added tests for handling missing nested paths and appropriate default values. - Improved handling of metadata prefixes in nested field access. * restore file |
||
|
|
738db9336e
|
[Feat] JWT - Sync user roles and team memberships when JWT Auth is used (#11994)
* add JWTLiteLLMRoleMap * test_sync_user_role_and_teams * add sync_user_role_and_teams * test_sync_user_role_and_teams * fix types * Sync User Roles and Teams with IDP * Add test for JWT role mapping to LiteLLM roles |
||
|
|
30b431681e
|
JWT Auth - correctly return user email + UI Model Update - Allow editing model access group for existing model (#11783)
* fix(handle_jwt.py): check user object, if jwt user is proxy admin correctly return user role - if jwt user has role updated in UI * test(test_handle_jwt.py): add unit test for passing correct user role * feat(model_info_view.tsx): separate UI component for updating edit model component * feat(model_info_view.tsx): allow updating model access group on UI show all available access groups in ui component * docs: minor fixes |
||
|
|
c40580f892
|
[Fix] JWT - Fix error when team member already part of team (#11735)
* fix _check_member_duplication * fix map_user_to_teams * test_map_user_to_teams_handles_already_in_team_exception * test_team_endpoints.py |
||
|
|
ef42461c1e
|
Litellm fix GitHub action testing (#11163)
* test: add __init__.py files * refactor: rename test folder to avoid naming conflict * test: update workflows * test: update tests * test: update imports * test: update tests * test: remove unused import * ci(test-litellm.yml): add pytest retry to github workflow * test: fix test |
Renamed from tests/litellm/proxy/auth/test_handle_jwt.py (Browse further)