Revert "(UI) - Security Improvement, move to JWT Auth for Admin UI Sessions (#8995)"

This reverts commit 01a44a4e47.
This commit is contained in:
Ishaan Jaff 2025-03-05 08:49:20 -08:00
parent 207f41cbea
commit 8d6815ce98
17 changed files with 539 additions and 1105 deletions

View file

@ -33,7 +33,6 @@ from litellm.proxy._types import (
ScopeMapping,
Span,
)
from litellm.proxy.management_helpers.ui_session_handler import UISessionHandler
from litellm.proxy.utils import PrismaClient, ProxyLogging
from .auth_checks import (
@ -407,60 +406,10 @@ class JWTHandler:
else:
return False
def _validate_ui_token(self, token: str) -> Optional[dict]:
"""
Helper function to validate tokens generated for the LiteLLM UI.
Returns the decoded payload if it's a valid UI token, None otherwise.
"""
import jwt
from litellm.proxy.proxy_server import master_key
try:
# Decode without verification to check if it's a UI token
unverified_payload = jwt.decode(token, options={"verify_signature": False})
# Check if this looks like a UI token (has specific claims that only UI tokens would have)
if UISessionHandler.is_ui_session_token(unverified_payload):
# This looks like a UI token, now verify it with the master key
if not master_key:
verbose_proxy_logger.debug(
"Missing LITELLM_MASTER_KEY for UI token validation"
)
return None
try:
payload = jwt.decode(
token,
master_key,
algorithms=["HS256"],
audience="litellm-ui",
leeway=self.leeway,
)
verbose_proxy_logger.debug(
"Successfully validated UI token for payload: %s",
json.dumps(payload, indent=4),
)
return payload
except jwt.InvalidTokenError as e:
verbose_proxy_logger.debug(f"Invalid UI token: {str(e)}")
raise ValueError(
f"Invalid UI token, Unable to validate token signature {str(e)}"
)
return None # Not a UI token
except Exception as e:
raise e
async def auth_jwt(self, token: str) -> dict:
# Supported algos: https://pyjwt.readthedocs.io/en/stable/algorithms.html
# "Warning: Make sure not to mix symmetric and asymmetric algorithms that interpret
# the key in different ways (e.g. HS* and RS*)."
ui_payload = self._validate_ui_token(token)
if ui_payload:
return ui_payload
algorithms = ["RS256", "RS384", "RS512", "PS256", "PS384", "PS512"]
audience = os.getenv("JWT_AUDIENCE")
@ -667,7 +616,6 @@ class JWTAuthManager:
user_id: Optional[str],
org_id: Optional[str],
api_key: str,
jwt_valid_token: dict,
) -> Optional[JWTAuthBuilderResult]:
"""Check admin status and route access permissions"""
if not jwt_handler.is_admin(scopes=scopes):
@ -677,7 +625,6 @@ class JWTAuthManager:
user_role=LitellmUserRoles.PROXY_ADMIN,
user_route=route,
litellm_proxy_roles=jwt_handler.litellm_jwtauth,
jwt_valid_token=jwt_valid_token,
)
if not is_allowed:
allowed_routes: List[Any] = jwt_handler.litellm_jwtauth.admin_allowed_routes
@ -751,7 +698,6 @@ class JWTAuthManager:
user_api_key_cache: DualCache,
parent_otel_span: Optional[Span],
proxy_logging_obj: ProxyLogging,
jwt_valid_token: dict,
) -> Tuple[Optional[str], Optional[LiteLLM_TeamTable]]:
"""Find first team with access to the requested model"""
@ -784,7 +730,6 @@ class JWTAuthManager:
user_role=LitellmUserRoles.TEAM,
user_route=route,
litellm_proxy_roles=jwt_handler.litellm_jwtauth,
jwt_valid_token=jwt_valid_token,
)
if is_allowed:
return team_id, team_object
@ -975,13 +920,7 @@ class JWTAuthManager:
# Check admin access
admin_result = await JWTAuthManager.check_admin_access(
jwt_handler=jwt_handler,
scopes=scopes,
route=route,
user_id=user_id,
org_id=org_id,
api_key=api_key,
jwt_valid_token=jwt_valid_token,
jwt_handler, scopes, route, user_id, org_id, api_key
)
if admin_result:
return admin_result
@ -1013,7 +952,6 @@ class JWTAuthManager:
user_api_key_cache=user_api_key_cache,
parent_otel_span=parent_otel_span,
proxy_logging_obj=proxy_logging_obj,
jwt_valid_token=jwt_valid_token,
)
# Get other objects