Skip to content

feat(sso): show full IdP claims in /sso/debug/callback - #27498

Merged
ryan-crabbe-berri merged 3 commits into
litellm_internal_stagingfrom
litellm_sso_debug_full_jwt_claims
May 9, 2026
Merged

feat(sso): show full IdP claims in /sso/debug/callback#27498
ryan-crabbe-berri merged 3 commits into
litellm_internal_stagingfrom
litellm_sso_debug_full_jwt_claims

Conversation

@ryan-crabbe-berri

@ryan-crabbe-berri ryan-crabbe-berri commented May 9, 2026

Copy link
Copy Markdown
Contributor

Summary

/sso/debug/callback only showed the proxy-parsed OpenID summary (id, email, display_name, team_ids, user_role). Customers using custom JWT claim mappings (team_id_jwt_field, team_alias_jwt_field, etc.) had no way to confirm what their IdP was actually returning — the very fields they configured were invisible in the debug page.

This PR renders two additional sections on the callback HTML:

  • Raw Claims (userinfo) — full IdP userinfo response.
  • Access Token Claims — decoded access-token JWT payload (when the IdP issues a JWT, not opaque).

Plus the existing OpenID summary, now labeled Parsed by Proxy.

Screenshots

before
before

after

after

Test plan

  • make test-unit for the SSO test module passes locally.
  • New: test_debug_sso_callback_renders_full_jwt_claims — asserts custom claims (team_id, team_alias, teams, roles) reach the rendered HTML, and a planted access_token / id_token in userinfo is stripped from the response body.
  • New: test_debug_sso_callback_handles_missing_raw_response — asserts Microsoft / Google paths (which don't return raw responses or access-token payloads today) still render successfully with empty raw-claims and access-token-claims sections.
  • End-to-end: ran /sso/debug/login -> Auth0 -> /sso/debug/callback against an Auth0 tenant with a Post-Login Action emitting custom claims; verified before/after rendering and bearer-token strip on a real flow.

Resolves LIT-2838

The debug callback only displayed the proxy-parsed OpenID summary, so
customers couldn't verify what custom claims (team_id, team_alias, roles,
etc.) the IdP was actually returning. Render two new sections — Raw
Claims (userinfo) and Access Token Claims (decoded JWT) — alongside the
existing parsed view. Strip bearer tokens defense-in-depth in case a
non-conforming IdP places them in its userinfo response.

Resolves LIT-2838
@codecov

codecov Bot commented May 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented May 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extends /sso/debug/callback to expose the full IdP-returned claims that were previously invisible to operators: a Raw Claims (userinfo) section showing the complete userinfo response and an Access Token Claims section with the decoded JWT payload, alongside the existing proxy-parsed fields (now labeled "Parsed by Proxy"). The XSS concern from the prior review thread is addressed: json.dumps output is passed through .replace('</', '\\/') before injection into the <script> block, and all client-side value rendering uses textContent rather than innerHTML.

  • ui_sso.py captures received_response and access_token_payload from get_generic_sso_response, applies a second-pass _OAUTH_TOKEN_FIELDS strip (defense-in-depth against non-conforming IdPs placing bearer tokens in userinfo), and passes a structured sso_payload dict to the template.
  • jwt_display_template.py adds two new rendered sections and refactors the JavaScript to drive all three display areas from the structured payload; Google/Microsoft paths safely render empty sections when the tuple values are None.
  • Two new mock-only tests verify custom claim surfacing, token stripping, and graceful empty-section handling.

Confidence Score: 5/5

Safe to merge — the debug endpoint only surfaces additional IdP claims that were already available server-side, token fields are stripped at two layers, and the template injection is escaped correctly against script-injection.

The change is well-scoped to the debug SSO endpoint, which is admin-only. The XSS concern raised in the prior review (unescaped </ in the embedded JSON script block) has been correctly addressed with the replace('</', '\/') call. Client-side rendering exclusively uses textContent, providing an additional layer of protection. Bearer token stripping is applied both inside get_generic_sso_response and again in debug_sso_callback, so a non-conforming IdP cannot leak credentials through either layer. The two new tests are mock-only and cover the key behavioral contracts.

No files require special attention.

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/ui_sso.py Captures received_response and access_token_payload from the generic SSO path, strips _OAUTH_TOKEN_FIELDS (defense-in-depth), and injects all three sections into the structured sso_payload; XSS mitigation via replace('</', '\/') is correctly applied before embedding the JSON into the script block.
litellm/proxy/common_utils/html_forms/jwt_display_template.py Adds "Raw Claims (userinfo)" and "Access Token Claims" sections alongside the renamed "Parsed by Proxy" section; JavaScript renders all values via textContent (safe from DOM-based XSS); layout and CSS additions are cosmetically clean.
tests/test_litellm/proxy/management_endpoints/test_ui_sso.py Two new mock-only async tests cover the generic SSO path (full claims + token stripping) and the Microsoft/Google path (empty sections); no real network calls, consistent with repo testing standards.

Reviews (2): Last reviewed commit: "Update litellm/proxy/management_endpoint..." | Re-trigger Greptile

Comment thread litellm/proxy/management_endpoints/ui_sso.py Outdated
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@ryan-crabbe-berri

Copy link
Copy Markdown
Contributor Author

@greptile re review

@ryan-crabbe-berri
ryan-crabbe-berri merged commit 13a1933 into litellm_internal_staging May 9, 2026
115 checks passed
@ryan-crabbe-berri
ryan-crabbe-berri deleted the litellm_sso_debug_full_jwt_claims branch May 9, 2026 00:39
fzowl pushed a commit to fzowl/litellm that referenced this pull request Jun 24, 2026
* feat(sso): show full IdP claims in /sso/debug/callback

The debug callback only displayed the proxy-parsed OpenID summary, so
customers couldn't verify what custom claims (team_id, team_alias, roles,
etc.) the IdP was actually returning. Render two new sections — Raw
Claims (userinfo) and Access Token Claims (decoded JWT) — alongside the
existing parsed view. Strip bearer tokens defense-in-depth in case a
non-conforming IdP places them in its userinfo response.

Resolves LIT-2838

* Update litellm/proxy/management_endpoints/ui_sso.py

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(sso): hoist json.dumps out of f-string for py3.10 ruff

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
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.

3 participants