From bd878e742e51380745b36b9ec2ce1eafe7d7d90f Mon Sep 17 00:00:00 2001 From: Hendrik Jaks Date: Thu, 19 Mar 2026 23:53:35 +0200 Subject: [PATCH] fix(ui): use dynamic cookie path based on server_root_path Hardcoded path=/ui breaks when LiteLLM is deployed with a custom server_root_path. Now derives the cookie path from serverRootPath so it works at /ui, /myapp/ui, etc. Also reuse clearTokenCookies() in deleteCookie() to avoid duplication. Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/litellm-dashboard/src/app/page.tsx | 10 ++-------- ui/litellm-dashboard/src/utils/cookieUtils.ts | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 8a748849baa..9946cc53925 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -43,7 +43,7 @@ import ToolPoliciesView from "@/components/ToolPoliciesView"; import SpendLogsTable from "@/components/view_logs"; import ViewUserDashboard from "@/components/view_users"; import { ThemeProvider } from "@/contexts/ThemeContext"; -import { getCookie } from "@/utils/cookieUtils"; +import { clearTokenCookies, getCookie } from "@/utils/cookieUtils"; import { isJwtExpired } from "@/utils/jwtUtils"; import { buildLoginUrlWithReturn, consumeReturnUrl, normalizeUrlForCompare, storeReturnUrl } from "@/utils/returnUrlUtils"; import { formatUserRole, isAdminRole } from "@/utils/roles"; @@ -56,14 +56,8 @@ import { ConfigProvider, theme } from "antd"; function deleteCookie(name: string, path = "/") { // Best-effort client-side clear (works for non-HttpOnly cookies without Domain) document.cookie = `${name}=; Max-Age=0; Path=${path}`; - // Also clear the JS-set cookie at /ui (used to work around HttpOnly proxies) if (name === "token") { - document.cookie = `${name}=; Max-Age=0; Path=/ui`; - try { - sessionStorage.removeItem(name); - } catch { - // sessionStorage may be unavailable - } + clearTokenCookies(); } } diff --git a/ui/litellm-dashboard/src/utils/cookieUtils.ts b/ui/litellm-dashboard/src/utils/cookieUtils.ts index c212f3b0a3b..928f640fc2d 100644 --- a/ui/litellm-dashboard/src/utils/cookieUtils.ts +++ b/ui/litellm-dashboard/src/utils/cookieUtils.ts @@ -1,6 +1,17 @@ /** * Utility functions for managing cookies */ +import { serverRootPath } from "@/components/networking"; + +/** + * Returns the cookie path for the UI. + * Respects server_root_path so the cookie works when LiteLLM is + * deployed behind a subpath (e.g. /myapp/ui instead of /ui). + */ +function getUiCookiePath(): string { + const root = serverRootPath === "/" ? "" : serverRootPath; + return `${root}/ui`; +} /** * Clears the token cookie from both root and /ui paths @@ -16,7 +27,8 @@ export function clearTokenCookies() { // Clear with various combinations of path and SameSite // Include current path in case of custom server root path const currentPath = window.location.pathname; - const paths = ["/", "/ui"]; + const uiCookiePath = getUiCookiePath(); + const paths = ["/", uiCookiePath]; // Add the current path directory if it's different from root and /ui if (currentPath && currentPath !== "/" && !currentPath.startsWith("/ui")) { @@ -70,7 +82,8 @@ export function storeLoginToken(token: string) { // is readable by getCookie() via document.cookie. try { const secure = window.location.protocol === "https:" ? "; Secure" : ""; - document.cookie = `token=${encodeURIComponent(token)}; path=/ui; SameSite=Lax${secure}`; + const cookiePath = getUiCookiePath(); + document.cookie = `token=${encodeURIComponent(token)}; path=${cookiePath}; SameSite=Lax${secure}`; } catch { // cookie setting may fail in restrictive environments }