From 0f2a416b6f92764300bdbb76a403b3d7030c5068 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Mon, 2 Mar 2026 13:59:16 -0800 Subject: [PATCH] fix(ui): Address Greptile review feedback on proxy switcher - Fix removeConnection to use setProxyBaseUrl(null) instead of setProxyBaseUrl(defaultConn.url), consistent with switchConnection - Replace hardcoded "Admin" role for remote proxies with actual role fetched from /user/info endpoint on the remote proxy Co-Authored-By: Claude Opus 4.6 --- .../app/(dashboard)/hooks/useAuthorized.ts | 48 +++++++++++++++++-- .../src/contexts/ProxyConnectionContext.tsx | 3 +- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.ts b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.ts index e45fb318e42..ce253143094 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.ts +++ b/ui/litellm-dashboard/src/app/(dashboard)/hooks/useAuthorized.ts @@ -1,11 +1,11 @@ "use client"; -import { getProxyBaseUrl } from "@/components/networking"; +import { getProxyBaseUrl, getGlobalLitellmHeaderName } from "@/components/networking"; import { clearTokenCookies, getCookie } from "@/utils/cookieUtils"; import { checkTokenValidity, decodeToken } from "@/utils/jwtUtils"; import { useProxyConnection } from "@/contexts/ProxyConnectionContext"; import { useRouter } from "next/navigation"; -import { useEffect, useMemo } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useUIConfig } from "./uiConfig/useUIConfig"; function formatUserRole(userRole: string) { @@ -52,6 +52,42 @@ const useAuthorized = () => { const isRemoteAuthorized = isRemoteProxy && !!activeConnection?.apiKey; const isAuthorized = isRemoteAuthorized || (isTokenValid && !uiConfig?.admin_ui_disabled); + // Fetch actual user info from remote proxy to get the real role + const [remoteUserInfo, setRemoteUserInfo] = useState<{ + user_role: string | null; + user_id: string | null; + user_email: string | null; + }>({ user_role: null, user_id: null, user_email: null }); + + useEffect(() => { + if (!isRemoteProxy || !activeConnection?.apiKey) return; + + const fetchRemoteUserInfo = async () => { + try { + const url = `${getProxyBaseUrl()}/user/info`; + const response = await fetch(url, { + method: "GET", + headers: { + [getGlobalLitellmHeaderName()]: `Bearer ${activeConnection.apiKey}`, + "Content-Type": "application/json", + }, + }); + if (response.ok) { + const data = await response.json(); + setRemoteUserInfo({ + user_role: data?.user_role ?? null, + user_id: data?.user_id ?? null, + user_email: data?.user_email ?? null, + }); + } + } catch { + // Silently fail — role will fall back to "Admin" + } + }; + + fetchRemoteUserInfo(); + }, [isRemoteProxy, activeConnection?.apiKey]); // eslint-disable-line react-hooks/exhaustive-deps + // Single useEffect for all redirect logic useEffect(() => { if (isLoading) return; @@ -74,9 +110,11 @@ const useAuthorized = () => { isAuthorized, token: isAuthorized ? token : null, accessToken: effectiveAccessToken, - userId: isRemoteProxy ? null : (decoded?.user_id ?? null), - userEmail: isRemoteProxy ? null : (decoded?.user_email ?? null), - userRole: isRemoteProxy ? "Admin" : formatUserRole(decoded?.user_role), + userId: isRemoteProxy ? (remoteUserInfo.user_id ?? null) : (decoded?.user_id ?? null), + userEmail: isRemoteProxy ? (remoteUserInfo.user_email ?? null) : (decoded?.user_email ?? null), + userRole: isRemoteProxy + ? formatUserRole(remoteUserInfo.user_role ?? "proxy_admin") + : formatUserRole(decoded?.user_role), premiumUser: decoded?.premium_user ?? null, disabledPersonalKeyCreation: decoded?.disabled_non_admin_personal_key_creation ?? null, showSSOBanner: isRemoteProxy ? false : decoded?.login_method === "username_password", diff --git a/ui/litellm-dashboard/src/contexts/ProxyConnectionContext.tsx b/ui/litellm-dashboard/src/contexts/ProxyConnectionContext.tsx index c3cde0be47c..80e126f3edf 100644 --- a/ui/litellm-dashboard/src/contexts/ProxyConnectionContext.tsx +++ b/ui/litellm-dashboard/src/contexts/ProxyConnectionContext.tsx @@ -160,7 +160,8 @@ export const ProxyConnectionProvider: React.FC = ( const defaultConn = connections.find((c) => c.isDefault); if (defaultConn) { saveActiveId(defaultConn.id); - setProxyBaseUrl(defaultConn.url); + // Clear the override so getProxyBaseUrl() falls back to window.location.origin + setProxyBaseUrl(null); window.location.reload(); } }