From 8babb8a46ec7795a3649123ae383febb64f70056 Mon Sep 17 00:00:00 2001 From: Tyler Gillispie Date: Mon, 24 Aug 2026 15:57:02 -0700 Subject: [PATCH] fix(session): clear invalid cookies on redirect and add cache-control headers to prevent 302 caching loops --- server/main.ts | 5 +++ ui/mod.ts | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/server/main.ts b/server/main.ts index 8ffb8b7..1389002 100644 --- a/server/main.ts +++ b/server/main.ts @@ -366,6 +366,8 @@ app.post("/api/guests/sandbox", async (c) => { const cookieDomain = getCookieDomain(rpID); + deleteCookie(c, "session_id", { path: "/" }); + setCookie(c, "session_id", sessionId, { domain: cookieDomain, path: "/", @@ -832,6 +834,9 @@ app.post("/api/login/verify", async (c) => { const cookieDomain = getCookieDomain(rpID); + // Clear any existing host-only cookie that might shadow the wildcard domain cookie + deleteCookie(c, "session_id", { path: "/" }); + setCookie(c, "session_id", sessionId, { domain: cookieDomain, path: "/", diff --git a/ui/mod.ts b/ui/mod.ts index 2c541b6..8be3060 100644 --- a/ui/mod.ts +++ b/ui/mod.ts @@ -30,6 +30,7 @@ const uiApp: Hono = new Hono(); // Explicit Side Effect: Route rendering uiApp.get("/", (c) => { + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); }); @@ -99,6 +100,7 @@ uiApp.get("/logout", async (c) => { if (safeRedirect) { return c.redirect(`/login?redirect=${encodeURIComponent(safeRedirect)}`); } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); }); @@ -123,6 +125,14 @@ uiApp.get("/register", (c) => { uiApp.get("/dashboard", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -152,6 +162,14 @@ uiApp.get("/dashboard", async (c) => { uiApp.get("/dashboard/sessions", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -172,6 +190,14 @@ uiApp.get("/dashboard/sessions", async (c) => { uiApp.get("/dashboard/passkeys", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -194,6 +220,14 @@ uiApp.get("/admin", (c) => { uiApp.get("/admin/users", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -214,6 +248,14 @@ uiApp.get("/admin/users", async (c) => { uiApp.get("/admin/apps", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -237,6 +279,14 @@ uiApp.get("/admin/apps", async (c) => { uiApp.get("/admin/roles", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -263,6 +313,14 @@ uiApp.get("/admin/roles", async (c) => { uiApp.get("/admin/invites", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -295,6 +353,14 @@ uiApp.get("/admin/invites", async (c) => { uiApp.get("/admin/aaguid", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -315,6 +381,14 @@ uiApp.get("/admin/aaguid", async (c) => { uiApp.get("/admin/users/:id", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); } @@ -378,6 +452,14 @@ uiApp.get("/admin/users/:id", async (c) => { uiApp.get("/admin/audit-logs", async (c) => { const auth = await getAuthenticatedUser(c); if (!auth) { + if (getCookie(c, "session_id")) { + deleteCookie(c, "session_id", { path: "/" }); // clear host cookie + deleteCookie(c, "session_id", { + domain: getCookieDomain(Deno.env.get("RP_ID")), + path: "/", + }); // clear domain cookie + } + c.header("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); return c.redirect("/login"); }