Skip to content

feat(auth): add scope and wildcard support for JWT routing overrides - #26325

Merged
oss-pr-review-agent-shin[bot] merged 3 commits into
shin_agent_oss_staging_05_07_2026from
litellm_jwt_override_scope_wildcard_reapply
May 7, 2026
Merged

feat(auth): add scope and wildcard support for JWT routing overrides#26325
oss-pr-review-agent-shin[bot] merged 3 commits into
shin_agent_oss_staging_05_07_2026from
litellm_jwt_override_scope_wildcard_reapply

Conversation

@milan-berri

@milan-berri milan-berri commented Apr 23, 2026

Copy link
Copy Markdown
Collaborator

Enhancement request for JWT routing_overrides to:

  • support optional scope selector matching (same optional behavior as client_id)
  • support wildcard matching for selectors (not full regex), with iss still required

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/test_litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem
  • I have requested a Greptile review by commenting @greptileai and received a Confidence Score of at least 4/5 before requesting a maintainer review

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
✅ Test

Changes

  • Added scope to JWTRoutingOverride schema:
    • scope: Optional[Union[str, List[str]]] = None
  • Extended routing override claim matching:
    • wildcard support with fnmatch (* and ?)
    • list-aware selector and claim matching
    • space-delimited string claim handling (notably for scope)
  • Updated override evaluation to include scope:
    • _matches_routing_override(...) now checks iss, optional client_id, optional scope, optional aud
  • Added/refined tests in tests/test_litellm/proxy/auth/test_user_api_key_auth.py:
    • parametrized matcher tests for exact/list/wildcard/scope tokenization semantics
    • parametrized override tests for combined selector behavior (AND semantics)
    • focused async routing tests for OAuth2 path vs JWT fallback behavior
  • Kept route-scope behavior unchanged:
    • JWT override to OAuth2 remains restricted to LLM + info routes (no expansion to management routes)

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 scope selector to JWTRoutingOverride and documents matching semantics (case-sensitive shell wildcards, and space-delimited parsing only for scope).

Updates JWT routing-override evaluation to support wildcard selectors (*, ?) across iss/client_id/aud and to AND in scope matching (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.

@veria-ai

veria-ai Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

JWT routing override selectors extended

This 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
Risk: 2/10

@greptile-apps

greptile-apps Bot commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends JWTRoutingOverride with an optional scope selector and adds shell-style wildcard matching (*/? via fnmatch.fnmatchcase) to all routing-override selectors. The implementation is clean: scope is space-tokenized only when explicitly requested via split_space_delimited=True, keeping iss/aud/client_id as full-string or wildcard matches without splitting, and backward compatibility is preserved since the new field is optional.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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)"]
Loading

Reviews (3): Last reviewed commit: "refactor(auth): drop dead fallback in sc..." | Re-trigger Greptile

Comment thread litellm/proxy/auth/user_api_key_auth.py
Comment thread litellm/proxy/auth/user_api_key_auth.py Outdated
@milan-berri
milan-berri temporarily deployed to integration-postgres April 23, 2026 10:02 — with GitHub Actions Inactive
@milan-berri
milan-berri temporarily deployed to integration-postgres April 23, 2026 10:02 — with GitHub Actions Inactive
@milan-berri
milan-berri temporarily deployed to integration-postgres April 23, 2026 10:02 — with GitHub Actions Inactive
@milan-berri
milan-berri temporarily deployed to integration-postgres April 23, 2026 10:02 — with GitHub Actions Inactive
@milan-berri
milan-berri temporarily deployed to integration-postgres April 23, 2026 10:02 — with GitHub Actions Inactive
…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
@milan-berri
milan-berri force-pushed the litellm_jwt_override_scope_wildcard_reapply branch from 36b556f to e96ea1b Compare April 28, 2026 08:46
milan-berri added a commit to milan-berri/litellm-docs that referenced this pull request Apr 28, 2026
…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

codecov Bot commented May 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@cursor cursor Bot left a comment

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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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

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.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit c3758db. Configure here.

@oss-pr-review-agent-shin
oss-pr-review-agent-shin Bot changed the base branch from litellm_internal_staging to shin_agent_oss_staging_05_07_2026 May 7, 2026 21:34
@oss-pr-review-agent-shin
oss-pr-review-agent-shin Bot merged commit bac03ac into shin_agent_oss_staging_05_07_2026 May 7, 2026
97 of 115 checks passed
@oss-pr-review-agent-shin

Copy link
Copy Markdown
Contributor

🤖 litellm-agent: Squash-merged into staging branch shin_agent_oss_staging_05_07_2026. Staging PR: #27422


Triage Summary
Extends JWT routing override matching to support a new scope claim selector and shell-style wildcard patterns (* and ?) for all selectors. Adds space-delimited tokenization for the OAuth/OIDC scope claim so multi-scope strings are matched token-by-token. Changes are confined to litellm/proxy/_types.py (adds scope field to JWTRoutingOverride), litellm/proxy/auth/user_api_key_auth.py (rewrites _routing_selector_matches_claim with fnmatch support and scope splitting), and a substantially expanded test suite in tests/test_litellm/proxy/auth/test_user_api_key_auth.py.

Merge Confidence: 5/5 ✅ READY
⚠️ 11 checks failing: ci/circleci: litellm_assistants_api_testing, ci/circleci: langfuse_logging_unit_tests, ci/circleci: batches_testing (+8 more)
Ready to ship.

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.

@oss-pr-review-agent-shin
oss-pr-review-agent-shin Bot deleted the litellm_jwt_override_scope_wildcard_reapply branch May 7, 2026 21:34
mubashir1osmani pushed a commit to BerriAI/litellm-docs that referenced this pull request May 21, 2026
…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).
mubashir1osmani added a commit to BerriAI/litellm-docs that referenced this pull request May 21, 2026
* 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>
mubashir1osmani added a commit to BerriAI/litellm-docs that referenced this pull request May 21, 2026
* 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>
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
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.

1 participant