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) <noreply@anthropic.com>
This commit is contained in:
Hendrik Jaks 2026-03-19 23:53:35 +02:00
parent a0937607a4
commit bd878e742e
2 changed files with 17 additions and 10 deletions

View file

@ -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();
}
}

View file

@ -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
}