diff --git a/docs/my-website/docs/proxy/cli_sso.md b/docs/my-website/docs/proxy/cli_sso.md index ad0f033f802..a20f8a313d4 100644 --- a/docs/my-website/docs/proxy/cli_sso.md +++ b/docs/my-website/docs/proxy/cli_sso.md @@ -52,6 +52,10 @@ LITELLM_CLI_JWT_EXPIRATION_HOURS=48 EXPERIMENTAL_UI_LOGIN="True" litellm --confi - `LITELLM_CLI_JWT_EXPIRATION_HOURS=168` - Tokens expire after 7 days (168 hours) - `LITELLM_CLI_JWT_EXPIRATION_HOURS=720` - Tokens expire after 30 days (720 hours) +:::note[Experimental UI Session] +When `EXPERIMENTAL_UI_LOGIN` is enabled, the **browser UI login** session uses a fixed 10-minute expiry (not configurable). `LITELLM_UI_SESSION_DURATION` applies only to non-experimental flows. +::: + :::tip You can check your current token's age and expiration status using: ```bash diff --git a/litellm/constants.py b/litellm/constants.py index 60f7dcdb729..48f3b049eb1 100644 --- a/litellm/constants.py +++ b/litellm/constants.py @@ -1315,8 +1315,8 @@ CLI_JWT_EXPIRATION_HOURS = int( ) ########################### UI SESSION DURATION ########################### -# Duration for UI login session (username/password, SSO). Format: "30s", "30m", "24h", "7d" -# Applies to default login flows (not EXPERIMENTAL_UI_LOGIN which uses its own 10-minute expiry) +# Duration for UI login session (username/password, SSO, invitation links). Format: "30s", "30m", "24h", "7d" +# Does NOT apply to EXPERIMENTAL_UI_LOGIN flow, which intentionally uses a fixed 10-minute expiry for security. LITELLM_UI_SESSION_DURATION = os.getenv("LITELLM_UI_SESSION_DURATION", "24h") ########################### DB CRON JOB NAMES ########################### diff --git a/tests/test_litellm/proxy/auth/test_auth_checks.py b/tests/test_litellm/proxy/auth/test_auth_checks.py index ad1ad299b27..4cdca2d0617 100644 --- a/tests/test_litellm/proxy/auth/test_auth_checks.py +++ b/tests/test_litellm/proxy/auth/test_auth_checks.py @@ -136,6 +136,29 @@ def test_get_experimental_ui_login_jwt_auth_token_uses_10_min_expiry( assert expires <= now + timedelta(minutes=10, seconds=2) +def test_experimental_ui_token_ignores_litellm_ui_session_duration( + valid_sso_user_defined_values, +): + """Regression test: LITELLM_UI_SESSION_DURATION must NOT affect Experimental UI token expiry. + Experimental UI intentionally uses fixed 10-min expiry. If this test fails, the constant + was incorrectly wired to the experimental flow.""" + # Default LITELLM_UI_SESSION_DURATION is "24h" - token must still expire in ~10 min + token = ExperimentalUIJWTToken.get_experimental_ui_login_jwt_auth_token( + valid_sso_user_defined_values + ) + decrypted_token = decrypt_value_helper( + token, key="ui_hash_key", exception_type="debug" + ) + assert decrypted_token is not None + token_data = json.loads(decrypted_token) + expires = datetime.fromisoformat(token_data["expires"].replace("Z", "+00:00")) + now = get_utc_datetime() + # Must be ~10 min, NOT 24h. If LITELLM_UI_SESSION_DURATION were incorrectly used, this would fail. + assert expires <= now + timedelta(minutes=11), ( + "Experimental UI must use 10-min expiry, not LITELLM_UI_SESSION_DURATION" + ) + + def test_get_experimental_ui_login_jwt_auth_token_invalid( invalid_sso_user_defined_values, ):