litellm-mirror/tests/litellm/proxy/auth/test_handle_jwt.py
Ishaan Jaff 01a44a4e47
(UI) - Security Improvement, move to JWT Auth for Admin UI Sessions (#8995)
* (UI) - Improvements to session handling logic  (#8970)

* add cookieUtils

* use utils for clearing cookies

* on logout use clearTokenCookies

* ui use correct clearTokenCookies

* navbar show userEmail on UserID page

* add timestamp on token cookie

* update generate_authenticated_redirect_response

* use common getAuthToken

* fix clearTokenCookies

* fixes for get auth token

* fix invitation link sign in logic

* Revert "fix invitation link sign in logic"

This reverts commit 30e5308cb3.

* fix getAuthToken

* update setAuthToken

* fix ui session handling

* fix ui session handler

* bug fix stop generating LiteLLM Virtual keys for access

* working JWT insert into cookies

* use central place to build UI JWT token

* add _validate_ui_token

* fix ui session handler

* fix fetchWithCredentials

* check allowed routes for ui session tokens

* expose validate_session endpoint

* validate session endpoint

* call sso/session/validate

* getUISessionDetails

* ui move to getUISessionDetails

* /sso/session/validate

* fix cookie utils

* use getUISessionDetails

* use ui_session_id

* "/spend/logs/ui" in spend_tracking_routes

* working sign in JWT flow for proxy admin

* allow proxy admin to access ui routes

* use check_route_access

* update types

* update login method

* fixes to ui session handler

* working flow for admin and internal users

* fixes for invite links

* use JWTs for SSO sign in

* fix /invitation/new flow

* fix code quality checks

* fix _get_ui_session_token_from_cookies

* /organization/list

* ui sso sign in

* TestUISessionHandler

* TestUISessionHandler
2025-03-04 21:48:23 -08:00

108 lines
3.6 KiB
Python

import datetime
import json
import os
import sys
from datetime import timezone
import pytest
from fastapi.testclient import TestClient
sys.path.insert(
0, os.path.abspath("../../../..")
) # Adds the parent directory to the system path
import jwt
from litellm.proxy.auth.handle_jwt import JWTHandler
from litellm.proxy.management_helpers.ui_session_handler import UISessionHandler
class TestJWTHandler:
@pytest.fixture
def jwt_handler(self):
handler = JWTHandler()
handler.leeway = 60 # Set leeway for testing
return handler
@pytest.fixture
def setup_mocks(self, monkeypatch):
# Mock master_key
test_master_key = "test_master_key"
monkeypatch.setattr("litellm.proxy.proxy_server.master_key", test_master_key)
# Mock UISessionHandler.is_ui_session_token to return True for our test token
def mock_is_ui_session_token(payload):
return "ui_session_id" in payload
monkeypatch.setattr(
UISessionHandler, "is_ui_session_token", mock_is_ui_session_token
)
return test_master_key
def test_validate_valid_ui_token(self, jwt_handler, setup_mocks):
# Setup
test_master_key = setup_mocks
# Create a valid UI token
valid_payload = {
"ui_session_id": "test_session_id",
"exp": datetime.datetime.now(tz=timezone.utc).timestamp() + 3600,
"iat": datetime.datetime.now(tz=timezone.utc).timestamp(),
"aud": "litellm-ui",
}
valid_token = jwt.encode(valid_payload, test_master_key, algorithm="HS256")
# Test valid UI token
result = jwt_handler._validate_ui_token(valid_token)
assert result is not None
assert result["ui_session_id"] == "test_session_id"
def test_validate_expired_ui_token(self, jwt_handler, setup_mocks):
# Setup
test_master_key = setup_mocks
# Create an expired UI token
expired_payload = {
"ui_session_id": "test_session_id",
"exp": datetime.datetime.now(tz=timezone.utc).timestamp() - 3600,
"iat": datetime.datetime.now(tz=timezone.utc).timestamp() - 7200,
"aud": "litellm-ui",
}
expired_token = jwt.encode(expired_payload, test_master_key, algorithm="HS256")
# Test expired UI token
with pytest.raises(ValueError, match="Invalid UI token"):
jwt_handler._validate_ui_token(expired_token)
def test_validate_invalid_signature_ui_token(self, jwt_handler, setup_mocks):
# Setup
test_master_key = setup_mocks
# Create a token with invalid signature
valid_payload = {
"ui_session_id": "test_session_id",
"exp": datetime.datetime.now(tz=timezone.utc).timestamp() + 3600,
"iat": datetime.datetime.now(tz=timezone.utc).timestamp(),
"aud": "litellm-ui",
}
invalid_token = jwt.encode(valid_payload, "wrong_key", algorithm="HS256")
# Test UI token with invalid signature
with pytest.raises(ValueError, match="Invalid UI token"):
jwt_handler._validate_ui_token(invalid_token)
def test_validate_non_ui_token(self, jwt_handler, setup_mocks):
# Setup
test_master_key = setup_mocks
# Create a non-UI token
non_ui_payload = {
"sub": "user123",
"exp": datetime.datetime.now(tz=timezone.utc).timestamp() + 3600,
}
non_ui_token = jwt.encode(non_ui_payload, test_master_key, algorithm="HS256")
# Test non-UI token
result = jwt_handler._validate_ui_token(non_ui_token)
assert result is None