fix(ui): set JS-accessible cookie at /ui path as HttpOnly workaround

sessionStorage alone is unreliable. Also set the token via
document.cookie at path=/ui — nginx only adds HttpOnly to server-set
Set-Cookie headers, so a JS-set cookie is always readable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hendrik Jaks 2026-03-19 23:50:02 +02:00
parent 01f8844114
commit a0937607a4
2 changed files with 21 additions and 6 deletions

View file

@ -56,7 +56,9 @@ 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 {

View file

@ -52,17 +52,30 @@ export function clearTokenCookies() {
}
/**
* Stores the login token in sessionStorage.
* This ensures the token is available even when a reverse proxy adds HttpOnly
* to server-set cookies, making them invisible to JavaScript.
* Stores the login token so the UI can read it even when a reverse proxy
* (e.g. nginx-ingress) adds HttpOnly to the server-set cookie.
*
* Note: sessionStorage is per-tab, so users behind an HttpOnly proxy must log
* in once per tab. We intentionally avoid localStorage here because it persists
* after browser close and is readable by any injected script (XSS).
* Strategy:
* 1. Set a JS-accessible cookie at path "/ui". Because nginx only modifies
* server-set Set-Cookie headers, a cookie created via document.cookie will
* never carry HttpOnly. Using path "/ui" avoids colliding with the
* server-set HttpOnly cookie at path "/".
* 2. Also store in sessionStorage as a secondary fallback.
*/
export function storeLoginToken(token: string) {
if (typeof window === "undefined") return;
if (!token || !token.trim()) return;
// 1. JS-accessible cookie at /ui — survives same-tab navigations and
// 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}`;
} catch {
// cookie setting may fail in restrictive environments
}
// 2. sessionStorage backup
try {
sessionStorage.setItem("token", token);
} catch {