From e3c9304e8de187e38f6290d76adbf2d25d9bbaba Mon Sep 17 00:00:00 2001 From: Syed Ali Abbas Rahil Date: Tue, 7 Jul 2026 20:57:47 +0900 Subject: [PATCH] fix(ui): derive UI cookie path from the last /ui segment getUiCookiePath matched the first /ui segment, but the UI mounts at the last one (SERVER_ROOT_PATH + /ui). For a root path that itself contains a /ui segment (e.g. SERVER_ROOT_PATH=/foo/ui/bar), it resolved the wrong path, so logout cleared the wrong cookie paths and left the scoped auth cookie in place. Use the last /ui match so both the stored login cookie and the logout clearing target the correct server root path. --- ui/litellm-dashboard/src/utils/cookieUtils.test.ts | 14 ++++++++++++++ ui/litellm-dashboard/src/utils/cookieUtils.ts | 9 ++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/ui/litellm-dashboard/src/utils/cookieUtils.test.ts b/ui/litellm-dashboard/src/utils/cookieUtils.test.ts index 99c7922644a..e83e32ac433 100644 --- a/ui/litellm-dashboard/src/utils/cookieUtils.test.ts +++ b/ui/litellm-dashboard/src/utils/cookieUtils.test.ts @@ -140,6 +140,20 @@ describe("cookieUtils", () => { vi.restoreAllMocks(); }); + it("should clear token cookie at the server root path when the root path contains a /ui segment", () => { + const originalLocation = window.location; + vi.stubGlobal("location", { ...originalLocation, pathname: "/foo/ui/bar/ui/" }); + + const cookieSpy = vi.spyOn(document, "cookie", "set"); + + clearTokenCookies(); + + expect(cookieSpy).toHaveBeenCalledWith(expect.stringContaining("path=/foo/ui/bar;")); + + vi.unstubAllGlobals(); + vi.restoreAllMocks(); + }); + it("should clear sessionStorage token", () => { sessionStorage.setItem("token", "stored-token"); clearTokenCookies(); diff --git a/ui/litellm-dashboard/src/utils/cookieUtils.ts b/ui/litellm-dashboard/src/utils/cookieUtils.ts index d4b2c6a2490..f8466257220 100644 --- a/ui/litellm-dashboard/src/utils/cookieUtils.ts +++ b/ui/litellm-dashboard/src/utils/cookieUtils.ts @@ -14,9 +14,12 @@ function getUiCookiePath(): string { if (typeof window === "undefined") return "/ui"; // Match "/ui" only as a full path segment (followed by "/" or end of string) // to avoid false matches like "/my-ui-tool/login" → "/my-ui". - const match = window.location.pathname.match(/\/ui(?=\/|$)/); - if (match && match.index !== undefined) { - return window.location.pathname.substring(0, match.index + 3); + // The UI mounts at the last "/ui" segment (SERVER_ROOT_PATH + "/ui"), so use the + // last match to stay correct when the root path itself contains a "/ui" segment. + const matches = [...window.location.pathname.matchAll(/\/ui(?=\/|$)/g)]; + const lastMatch = matches[matches.length - 1]; + if (lastMatch && lastMatch.index !== undefined) { + return window.location.pathname.substring(0, lastMatch.index + 3); } return "/ui"; }