From 2923f350f89efdc5a74ba4a1b773c666bd30babe Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Thu, 28 May 2026 16:26:32 +0200 Subject: [PATCH] auth: fix HTTP login loop (Secure=false default, gate dashboard flash) --- containers/clearview/site/app.js | 36 +++++++++++++------ containers/clearview/site/styles.css | 5 +++ .../clearview/src/clearview_app/config.py | 4 ++- docs/changelog-develop.md | 6 ++++ 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/containers/clearview/site/app.js b/containers/clearview/site/app.js index 8c68b08..6664986 100644 --- a/containers/clearview/site/app.js +++ b/containers/clearview/site/app.js @@ -1,29 +1,45 @@ // ------------------------------------------------------------------------- -// Auth gate: runs before the main app IIFE bootstraps. If the user is not -// authenticated, we redirect to /login.html (or /setup.html when the backend -// indicates the initial setup is still required) and abort further init. +// Auth gate: must complete before the main app IIFE renders. While the +// gate is in flight, `` hides the UI via CSS so the +// dashboard never flashes for an unauthenticated user. // ------------------------------------------------------------------------- -(async function authGate() { +document.documentElement.dataset.authPending = '1'; + +window.__authReady = (async function authGate() { try { const r = await fetch('/api/auth/me', { credentials: 'same-origin' }); if (r.status === 401) { const setup = await fetch('/api/auth/setup-required').then(function (x) { return x.json(); }).catch(function () { return { setup_required: false }; }); window.location.replace(setup.setup_required ? '/setup.html' : '/login.html'); - return; + return false; + } + if (!r.ok) { + window.location.replace('/login.html'); + return false; } - if (!r.ok) return; const me = await r.json(); window.__clearviewUser = me; - renderUserBadge(me); - if (me.role !== 'admin') { - const usersLink = document.querySelector('[data-route="users"]'); - if (usersLink) usersLink.style.display = 'none'; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function () { applyAuthUi(me); }, { once: true }); + } else { + applyAuthUi(me); } + delete document.documentElement.dataset.authPending; + return true; } catch (e) { window.location.replace('/login.html'); + return false; } })(); +function applyAuthUi(me) { + renderUserBadge(me); + if (me.role !== 'admin') { + const usersLink = document.querySelector('[data-route="users"]'); + if (usersLink) usersLink.style.display = 'none'; + } +} + function renderUserBadge(me) { const slot = document.getElementById('userBadge'); if (!slot) return; diff --git a/containers/clearview/site/styles.css b/containers/clearview/site/styles.css index a21b16f..fb5fe4f 100644 --- a/containers/clearview/site/styles.css +++ b/containers/clearview/site/styles.css @@ -753,6 +753,11 @@ strong { } } +/* Hide the SPA until the auth gate resolves — prevents the unauthenticated + dashboard flash before the redirect kicks in. Login/setup pages don't load + app.js, so they are unaffected. */ +html[data-auth-pending] body { visibility: hidden; } + /* === Auth (login / setup) pages and header badge ============================== */ .auth-page { display: flex; align-items: center; justify-content: center; diff --git a/containers/clearview/src/clearview_app/config.py b/containers/clearview/src/clearview_app/config.py index c530f23..289667c 100644 --- a/containers/clearview/src/clearview_app/config.py +++ b/containers/clearview/src/clearview_app/config.py @@ -39,5 +39,7 @@ SCAN_MAX_ITEMS_PER_LIST = _int_env("SCAN_MAX_ITEMS_PER_LIST", 10000) # Auth cookie settings (override via env) COOKIE_NAME = "clearview_session" -COOKIE_SECURE = os.environ.get("COOKIE_SECURE", "true").lower() != "false" +# Local-only HTTP deployment: default to non-Secure cookies. Set +# COOKIE_SECURE=true if the stack ever sits behind HTTPS. +COOKIE_SECURE = os.environ.get("COOKIE_SECURE", "false").lower() == "true" COOKIE_SAMESITE = "lax" diff --git a/docs/changelog-develop.md b/docs/changelog-develop.md index 20195d8..196abcf 100644 --- a/docs/changelog-develop.md +++ b/docs/changelog-develop.md @@ -2,6 +2,12 @@ This file documents changes on the develop branch of this project. +## 2026-05-28 — Authentication: fix login loop on HTTP-only deployment + +### Changed +- `COOKIE_SECURE` now defaults to `false` (the stack runs HTTP-only locally; with `Secure` set, browsers silently drop the session cookie and the user enters a redirect loop). Set `COOKIE_SECURE=true` if the stack is ever fronted by HTTPS. +- SPA boot now hides the dashboard until `/api/auth/me` resolves (via `html[data-auth-pending]` + body visibility), eliminating the brief dashboard flash that appeared before the redirect. + ## 2026-05-28 — Authentication: align login/setup/modal styling with site light theme ### Changed