feat(auth): add scope and wildcard support for JWT routing overrides - #26325
Conversation
JWT routing override selectors extendedThis PR adds scope matching and case-sensitive wildcard support to JWT routing overrides. I checked the selector matching path, the auth-path handoff, and the configuration model; the unverified claims are still only used to select the validator, with final token validation left to the selected OAuth2/JWT auth flow. Status: 0 open |
Greptile SummaryThis PR extends Confidence Score: 5/5Safe to merge — all new fields are optional, routing still operates on unverified claims only for path selection, and final auth validation is unchanged. No P0 or P1 findings. Previously flagged items (bracket-expression edge case in fnmatch, unreachable else branch) have been resolved or acknowledged. Tests are mock-only, comprehensive, and correctly reflect the documented semantics. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/proxy/_types.py | Adds scope: Optional[Union[str, List[str]]] field to JWTRoutingOverride and extends the class docstring to document wildcard semantics and space-delimited tokenization. Change is backward-compatible (field is Optional). |
| litellm/proxy/auth/user_api_key_auth.py | Extends _routing_selector_matches_claim with split_space_delimited flag and fnmatch-based wildcard matching; wires scope into _matches_routing_override with the flag enabled. Logic is correct and the iss injection-via-space edge case is handled by the False-when-no-split guard. |
| tests/test_litellm/proxy/auth/test_user_api_key_auth.py | Adds comprehensive parametrized unit tests for the new matching logic and three async integration tests that verify the OAuth2 routing path is taken or skipped correctly; JWT token payloads are consistent with the configured overrides and no real network calls are made. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["Bearer token received"] --> B["_should_route_jwt_to_oauth2_override()"]
B --> C{"routing_overrides configured?"}
C -- No --> D["JWT auth path"]
C -- Yes --> E["get_unverified_claims(token)"]
E --> F{"For each override: _matches_routing_override()"}
F --> G["_routing_selector_matches_claim(iss, ...)"]
G --> H["_routing_selector_matches_claim(client_id, ...)"]
H --> I["_routing_selector_matches_claim(scope, split_space_delimited=True)"]
I --> J["_routing_selector_matches_claim(aud, ...)"]
J --> K{All selectors match?}
K -- No, next override --> F
K -- No overrides left --> D
K -- Yes --> L["OAuth2 auth path (Oauth2Handler.check_oauth2_token)"]
Reviews (3): Last reviewed commit: "refactor(auth): drop dead fallback in sc..." | Re-trigger Greptile
…25939) Extend routing_overrides with: - optional `scope` selector (same optional semantics as `client_id`) - shell-style wildcard matching (`*`, `?`) via fnmatchcase on all selectors - space-delimited scope tokenization, gated to the `scope` claim only to avoid iss/client_id injection risk Refactor _routing_selector_matches_claim to handle list claims, wildcards, and scope-only space splitting. _matches_routing_override now checks iss, optional client_id, optional scope, optional aud. Made-with: Cursor
The elif guard already requires `" " in claim_value.strip()`, which guarantees at least two non-empty tokens survive the split+filter, so `len(split_values) > 1` is always true. Collapses the ternary into a single comprehension (per Greptile review). Made-with: Cursor
36b556f to
e96ea1b
Compare
…ides Backfills the `BerriAI/litellm-docs` site with the changes that originally shipped under `docs/my-website/` in BerriAI/litellm#25939 / #26325. After the docs source was migrated to this repo, those edits could no longer be carried in the code PR and were dropped on rebase. - proxy/token_auth.md: expand "Matching behavior" with AND semantics, the new optional `scope` selector, list/string forms, shell-style wildcards (`*`, `?`, case-sensitive), and the scope-only space-split rule (iss/aud/client_id are never split on spaces). Adds a worked example combining `scope` with a wildcard `client_id`. - proxy/oauth2.md: cross-reference the new wildcard/scope behavior and point readers to token_auth.md for full details. Code change is in BerriAI/litellm#26325 (litellm_internal_staging).
…_wildcard_reapply Resolve test_user_api_key_auth.py conflicts: combine imports for JWT routing override helpers with staging's budget/route helpers; keep parametrized JWT routing tests and staging custom Litellm key header tests. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit c3758db. Configure here.
| # NOTE: wildcard matching is case-sensitive (fnmatch.fnmatchcase). | ||
| if "*" in selector or "?" in selector: | ||
| return fnmatch.fnmatchcase(claim, selector) | ||
| return selector == claim |
There was a problem hiding this comment.
Undocumented fnmatch bracket patterns silently active in wildcards
Low Severity
The wildcard gate (if "*" in selector or "?" in selector) only checks for * and ?, but fnmatch.fnmatchcase also interprets [seq] and [!seq] bracket patterns. A selector like "*[v2]" would treat [v2] as a character class (matching v or 2), not as the literal string [v2]. The docstring similarly documents only * and ? as supported. This creates inconsistent behavior: [seq] in a selector is literal when alone but interpreted as a pattern when * or ? is also present.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c3758db. Configure here.
bac03ac
into
shin_agent_oss_staging_05_07_2026
|
🤖 litellm-agent: Squash-merged into staging branch Triage Summary Merge Confidence: 5/5 ✅ READY Greptile 5/5, no blocking pattern findings, CircleCI passed. 11 checks failing but unrelated to this diff: ci/circleci: litellm_assistants_api_testing, ci/circleci: langfuse_logging_unit_tests, ci/circleci: batches_testing (+8 more). 10 unrelated CI failures unique to this PR (ci/circleci: litellm_assistants_api_testing, ci/circleci: langfuse_logging_unit_tests, ci/circleci: batches_testing (+7 more)) — not related to this diff but worth a glance. 1 check also red on neighboring PRs (ci/circleci: realtime_translation_testing) — infra-wide noise, no penalty. |
…ides (#31) Backfills the `BerriAI/litellm-docs` site with the changes that originally shipped under `docs/my-website/` in BerriAI/litellm#25939 / #26325. After the docs source was migrated to this repo, those edits could no longer be carried in the code PR and were dropped on rebase. - proxy/token_auth.md: expand "Matching behavior" with AND semantics, the new optional `scope` selector, list/string forms, shell-style wildcards (`*`, `?`, case-sensitive), and the scope-only space-split rule (iss/aud/client_id are never split on spaces). Adds a worked example combining `scope` with a wildcard `client_id`. - proxy/oauth2.md: cross-reference the new wildcard/scope behavior and point readers to token_auth.md for full details. Code change is in BerriAI/litellm#26325 (litellm_internal_staging).
* docs: add LLM-as-a-Judge guardrail guide with screenshots New guardrail type that uses an LLM to score responses against weighted criteria. Includes UI walkthrough, YAML config examples, blocked/passed response examples, and configuration reference. * docs(llm-judge): replace placeholder screenshots with real spend logs UI screenshots * docs(llm-judge): use real spend logs UI screenshots for blocked/passed guardrail views * docs(blog): make /blog responsive (#51) * docs(blog): make /blog responsive - Add mobile styles to swizzled BlogListPage (hero, marquee, posts, pagination). - Fix horizontal overflow caused by the marquee's white-space: nowrap propagating width up the flex chain. Break it with min-width: 0 on .page and #__docusaurus > *, plus defensive overflow-x: clip. - Respect prefers-reduced-motion (stop marquee animation). * Remove global overflow prevention styles Removed global styles to prevent horizontal overflow on mobile. * docs(proxy): add Grafana Cloud Pyroscope user and API token configuration options (#52) * docs(auth): document scope and wildcard support for JWT routing overrides (#31) Backfills the `BerriAI/litellm-docs` site with the changes that originally shipped under `docs/my-website/` in BerriAI/litellm#25939 / #26325. After the docs source was migrated to this repo, those edits could no longer be carried in the code PR and were dropped on rebase. - proxy/token_auth.md: expand "Matching behavior" with AND semantics, the new optional `scope` selector, list/string forms, shell-style wildcards (`*`, `?`, case-sensitive), and the scope-only space-split rule (iss/aud/client_id are never split on spaces). Adds a worked example combining `scope` with a wildcard `client_id`. - proxy/oauth2.md: cross-reference the new wildcard/scope behavior and point readers to token_auth.md for full details. Code change is in BerriAI/litellm#26325 (litellm_internal_staging). * docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) (#184) * docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) * docs(mcp): complete auth_type table, OAuth config reference, RBAC intersection model, hub-vs-public-internet distinction * docs(a2a): document x-litellm-api-key, trace-id enforcement, sub-agent propagation, agent access groups and full intersection model * docs(bedrock_agentcore): add LiteLLM A2A Gateway section — fixes broken anchor from a2a.md, documents dual JWT/SigV4 auth modes and full credential chain * docs: add AuthN/AuthZ overview page side-by-siding MCP and A2A gateways * docs(fixup): corrections from code-review pass — verified against current LiteLLM source * make changes --------- Co-authored-by: michelligabriele <gabriele.michelli@icloud.com> --------- Co-authored-by: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Co-authored-by: harish-berri <harish@berri.ai> Co-authored-by: milan-berri <milan@berri.ai> Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com> Co-authored-by: michelligabriele <gabriele.michelli@icloud.com>
* docs(blog): make /blog responsive (#51) * docs(blog): make /blog responsive - Add mobile styles to swizzled BlogListPage (hero, marquee, posts, pagination). - Fix horizontal overflow caused by the marquee's white-space: nowrap propagating width up the flex chain. Break it with min-width: 0 on .page and #__docusaurus > *, plus defensive overflow-x: clip. - Respect prefers-reduced-motion (stop marquee animation). * Remove global overflow prevention styles Removed global styles to prevent horizontal overflow on mobile. * docs(proxy): add Grafana Cloud Pyroscope user and API token configuration options (#52) * docs(auth): document scope and wildcard support for JWT routing overrides (#31) Backfills the `BerriAI/litellm-docs` site with the changes that originally shipped under `docs/my-website/` in BerriAI/litellm#25939 / #26325. After the docs source was migrated to this repo, those edits could no longer be carried in the code PR and were dropped on rebase. - proxy/token_auth.md: expand "Matching behavior" with AND semantics, the new optional `scope` selector, list/string forms, shell-style wildcards (`*`, `?`, case-sensitive), and the scope-only space-split rule (iss/aud/client_id are never split on spaces). Adds a worked example combining `scope` with a wildcard `client_id`. - proxy/oauth2.md: cross-reference the new wildcard/scope behavior and point readers to token_auth.md for full details. Code change is in BerriAI/litellm#26325 (litellm_internal_staging). * docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) (#184) * docs(mcp,a2a): code-verified auth reference fixes + overview page (#156) * docs(mcp): complete auth_type table, OAuth config reference, RBAC intersection model, hub-vs-public-internet distinction * docs(a2a): document x-litellm-api-key, trace-id enforcement, sub-agent propagation, agent access groups and full intersection model * docs(bedrock_agentcore): add LiteLLM A2A Gateway section — fixes broken anchor from a2a.md, documents dual JWT/SigV4 auth modes and full credential chain * docs: add AuthN/AuthZ overview page side-by-siding MCP and A2A gateways * docs(fixup): corrections from code-review pass — verified against current LiteLLM source * make changes --------- Co-authored-by: michelligabriele <gabriele.michelli@icloud.com> * Update Claude Code compatibility matrix (#175) litellm_version: v1.83.14-stable claude_code_version: 2.1.126 generated_at: 2026-05-20T06:09:45Z Co-authored-by: litellm-compat-matrix-bot <litellm-bot@berri.ai> * docs(mcp): pass guardrails via extra_body in OpenAI SDK example (#188) The OpenAI Python SDK rejects guardrails as a top-level argument; use extra_body to send LiteLLM-specific params to the proxy. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(release_notes): add v1.84.1 and v1.85.1 patch release notes (#191) Patch releases on top of v1.84.0 and v1.85.0, each shipping the same three PRs: Gemini 3.5 Flash day-0 support (#28268), a Vertex AI tool-calling fix for Gemini 3.5+ HTTP 400 errors (#28324), and a cross-pod spend-counter seeding fix (#27854). Adds release_notes/v1.84.1/ and release_notes/v1.85.1/ pages and updates the release_notes overview (Latest Release block + table). * docs: replace slow Inkeep search with offline @easyops-cn/docusaurus-search-local Inkeep search was reported as slow and exhibited focus / Cmd+K bugs (cursor in the wrong place, page preventing repeated searches). Swap the navbar SearchBar over to @easyops-cn/docusaurus-search-local, which builds a static lunr index at build time and renders results instantly on the client. - Add @easyops-cn/docusaurus-search-local theme with both docs and release_notes routes indexed. - Keep stop words and stems (technical docs frequently search short tokens) and enable highlight-on-target-page. - Drop the SearchBar config from @inkeep/cxkit-docusaurus so it only provides the floating Ask AI chat button (still useful for AI Q&A). Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com> --------- Co-authored-by: Cesar Garcia <128240629+Chesars@users.noreply.github.com> Co-authored-by: harish-berri <harish@berri.ai> Co-authored-by: milan-berri <milan@berri.ai> Co-authored-by: mubashir1osmani <mubashir.osmani777@gmail.com> Co-authored-by: michelligabriele <gabriele.michelli@icloud.com> Co-authored-by: agent-shin <279878236+agent-shin@users.noreply.github.com> Co-authored-by: litellm-compat-matrix-bot <litellm-bot@berri.ai> Co-authored-by: Sameer Kankute <sameer@berri.ai> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
…erriAI#26325) Squash-merged by litellm-agent from milan-berri's PR.


Enhancement request for JWT
routing_overridesto:scopeselector matching (same optional behavior asclient_id)issstill requiredPre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
tests/test_litellm/directory, Adding at least 1 test is a hard requirement - see detailsmake test-unit@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewCI (LiteLLM team)
Branch creation CI run
Link:
CI run for the last commit
Link:
Merge / cherry-pick CI run
Links:
Type
🆕 New Feature
✅ Test
Changes
scopetoJWTRoutingOverrideschema:scope: Optional[Union[str, List[str]]] = Nonefnmatch(*and?)scope)scope:_matches_routing_override(...)now checksiss, optionalclient_id, optionalscope, optionalaudtests/test_litellm/proxy/auth/test_user_api_key_auth.py:Note
Medium Risk
Changes JWT routing-override matching used to decide whether JWT-shaped bearer tokens are routed to OAuth2 introspection, so misconfiguration or matcher edge cases could affect authentication behavior.
Overview
Adds a new optional
scopeselector toJWTRoutingOverrideand documents matching semantics (case-sensitive shell wildcards, and space-delimited parsing only forscope).Updates JWT routing-override evaluation to support wildcard selectors (
*,?) acrossiss/client_id/audand to AND inscopematching (including splitting OAuth/OIDC scope strings). Expands unit/integration tests to cover exact/list/wildcard behavior, scope tokenization, and OAuth2-routing vs JWT-fallback flows.Reviewed by Cursor Bugbot for commit c3758db. Bugbot is set up for automated code reviews on this repo. Configure here.