Skip to content

fix(sso): handle opaque access tokens in process_sso_jwt_access_token#20726

Merged
krrishdholakia merged 1 commit intoBerriAI:litellm_oss_staging_02_09_2026from
ZeroClover:ZeroClover/fix-sso-opaque-token
Feb 10, 2026
Merged

fix(sso): handle opaque access tokens in process_sso_jwt_access_token#20726
krrishdholakia merged 1 commit intoBerriAI:litellm_oss_staging_02_09_2026from
ZeroClover:ZeroClover/fix-sso-opaque-token

Conversation

@ZeroClover
Copy link
Contributor

Relevant issues

Fixes #20724

Pre-Submission checklist

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

  • I have Added testing in the tests/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

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

🐛 Bug Fix

Changes

  • PR fix(sso): extract user roles from JWT access token for Keycloak compa… #20591 removed the sso_jwt_handler guard in process_sso_jwt_access_token(), causing jwt.decode() to run unconditionally on all access tokens. OIDC providers that return opaque (non-JWT) tokens (e.g. Logto) trigger a DecodeError, crashing the /sso/callback endpoint with a 500 error.
  • Wrap jwt.decode() in a try-except jwt.exceptions.DecodeError and return early when the token is not a valid JWT. This is safe because user info (team IDs, role) is already extracted from the UserInfo endpoint in generic_response_convertor(); the JWT access token extraction is only a supplementary source for providers like Keycloak that embed claims in the access token.
  • Clean up tests: remove two mock-based tests that were superseded by the PyJWT-based valid JWT generation tests, and fix the exception propagation test to use InvalidKeyError instead of a generic Exception, accurately reflecting that only DecodeError is caught.

OIDC providers like Logto may return opaque (non-JWT) access tokens,
which caused jwt.decode() to raise DecodeError and crash the SSO
callback with a 500 error. Catch DecodeError and skip JWT-based
extraction gracefully, since user info is already available from
the UserInfo endpoint.

Fixes BerriAI#20724
@vercel
Copy link

vercel bot commented Feb 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 8, 2026 9:39pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 8, 2026

Greptile Overview

Greptile Summary

Fixes a 500 error crash on the /sso/callback endpoint when OIDC providers return opaque (non-JWT) access tokens. After PR #20591 removed the sso_jwt_handler guard, jwt.decode() was running unconditionally on all tokens, causing DecodeError for providers like Logto that use opaque tokens.

  • Wraps jwt.decode() in try-except to catch jwt.exceptions.DecodeError and return early when token isn't a valid JWT
  • This is safe because user info (team IDs, role) is already extracted from the UserInfo endpoint; JWT access token extraction is supplementary for providers like Keycloak that embed claims in the access token
  • Cleans up test suite: removes two obsolete mock-based tests that were superseded by real PyJWT-based tests, and improves exception propagation test to use InvalidKeyError instead of generic Exception
  • Adds comprehensive test coverage for opaque token handling and real JWT scenarios

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The fix correctly addresses a production crash with minimal code changes (7 lines), uses appropriate exception handling (only catches DecodeError, not all exceptions), maintains backward compatibility, and includes comprehensive test coverage including real JWT scenarios and opaque token handling. The early return pattern is clean and the debug logging aids troubleshooting.
  • No files require special attention

Important Files Changed

Filename Overview
litellm/proxy/management_endpoints/ui_sso.py Wraps jwt.decode() in try-except to handle opaque (non-JWT) tokens gracefully, fixing crash on /sso/callback endpoint
tests/test_litellm/proxy/management_endpoints/test_ui_sso.py Removes obsolete mock-based tests, adds real JWT tests for opaque token handling and improved exception test accuracy

Sequence Diagram

sequenceDiagram
    participant Client
    participant SSO_Callback as /sso/callback
    participant process_sso_jwt as process_sso_jwt_access_token()
    participant JWT_Lib as jwt.decode()
    participant UserInfo as UserInfo Endpoint
    
    Client->>SSO_Callback: OAuth callback with access_token
    SSO_Callback->>UserInfo: Fetch user info (team IDs, role)
    UserInfo-->>SSO_Callback: User info extracted
    
    SSO_Callback->>process_sso_jwt: Extract additional claims from access_token
    
    alt Access token is a valid JWT
        process_sso_jwt->>JWT_Lib: Decode token (verify_signature=false)
        JWT_Lib-->>process_sso_jwt: JWT payload
        process_sso_jwt->>process_sso_jwt: Extract team_ids (via sso_jwt_handler)
        process_sso_jwt->>process_sso_jwt: Extract user_role (if not in UserInfo)
        process_sso_jwt-->>SSO_Callback: Updated result with JWT claims
    else Access token is opaque (non-JWT)
        process_sso_jwt->>JWT_Lib: Attempt decode
        JWT_Lib-->>process_sso_jwt: DecodeError
        Note over process_sso_jwt: Catch DecodeError, log debug, return early
        process_sso_jwt-->>SSO_Callback: Result unchanged (UserInfo data only)
    end
    
    SSO_Callback-->>Client: Authentication success
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@ZeroClover
Copy link
Contributor Author

@ishaan-jaff @Sameerlite

Would it be possible to prioritize reviewing and merging this PR? The issue caused by #20724 blocks all SSO through IdPs that do not use JWT Access Tokens, so this may be considered a fairly severe and urgent issue.

@superpoussin22
Copy link
Contributor

Think it could solve a issue I saw last Friday and I revert back to an old version due to another bug but didn’t get time to investigate @krrishdholakia @ishaan-jaff

@krrishdholakia krrishdholakia changed the base branch from main to litellm_oss_staging_02_09_2026 February 10, 2026 02:57
@krrishdholakia krrishdholakia merged commit 5d4cef2 into BerriAI:litellm_oss_staging_02_09_2026 Feb 10, 2026
7 of 8 checks passed
Sameerlite pushed a commit that referenced this pull request Feb 10, 2026
…#20726)

OIDC providers like Logto may return opaque (non-JWT) access tokens,
which caused jwt.decode() to raise DecodeError and crash the SSO
callback with a 500 error. Catch DecodeError and skip JWT-based
extraction gracefully, since user info is already available from
the UserInfo endpoint.

Fixes #20724
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.

[Bug]: SSO login fails with opaque access tokens

3 participants