diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index f2c70306f3..664eb3c172 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -38,8 +38,7 @@ export default function LoginPage() { if (data.nodeVersion) setNodeVersion(data.nodeVersion); if (data.nodeCompatible === false) setNodeCompatible(false); if (data.authenticated === true || data.requireLogin === false) { - router.push("/dashboard"); - router.refresh(); + window.location.href = "/dashboard"; return; } setHasPassword(!!data.hasPassword); @@ -77,13 +76,12 @@ export default function LoginPage() { if (res.ok) { sessionStorage.setItem("omniroute_login_time", String(Date.now())); - router.push("/dashboard"); - router.refresh(); + window.location.href = "/dashboard"; } else { const data = await res.json(); // (#521) If no password is set, redirect to onboarding instead of showing an error if (data.needsSetup) { - router.push("/dashboard/onboarding"); + window.location.href = "/dashboard/onboarding"; return; } setError(data.error || t("invalidPassword")); diff --git a/tests/unit/login-11143.test.ts b/tests/unit/login-11143.test.ts new file mode 100644 index 0000000000..f55f2edecf --- /dev/null +++ b/tests/unit/login-11143.test.ts @@ -0,0 +1,21 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import fs from "node:fs"; +import path from "node:path"; + +test("login page performs full window.location navigation after authentication to avoid cookie race", () => { + const loginPagePath = path.resolve(process.cwd(), "src/app/login/page.tsx"); + const content = fs.readFileSync(loginPagePath, "utf8"); + + // Ensure router.push("/dashboard") is replaced with window.location.href + assert.equal( + content.includes('router.push("/dashboard")'), + false, + "LoginPage should not use router.push('/dashboard') after login" + ); + assert.equal( + content.includes('window.location.href = "/dashboard"'), + true, + "LoginPage must perform full window.location navigation after login" + ); +});