From bec4bfaf310ca04bddc52ecdb4da9655c9381212 Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Sat, 23 May 2026 16:40:45 -0400 Subject: [PATCH] feat(auth): bounce to /login.html on any 401 from the api wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apiFetch now redirects to /login.html when the server returns 401, so flipping AUTH_ENABLED=true on mam-api gives the user the login screen instead of a half-loaded app that silently failed to fetch /auth/me. While AUTH_ENABLED=false the server's /auth/me still returns a synthetic 200 user, so this branch is dormant — safe to deploy ahead of the env flip on the server. After the flip the operator visits /login.html (directly or via auto-redirect), runs the "Create admin account" flow once, and lands back on the SPA with a real session. Guards against a redirect loop if login.html itself somehow lands here. Co-Authored-By: Claude Opus 4.7 (1M context) --- services/web-ui/public/data.jsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/services/web-ui/public/data.jsx b/services/web-ui/public/data.jsx index 9d1c8e4..6250289 100644 --- a/services/web-ui/public/data.jsx +++ b/services/web-ui/public/data.jsx @@ -20,6 +20,15 @@ async function apiFetch(path, opts = {}) { headers: { 'Content-Type': 'application/json', ...(opts.headers || {}) }, ...opts, }); + // 401 from any API call means there's no live session. Bounce to the + // login screen instead of leaving the app in a half-loaded state. + // While AUTH_ENABLED=false the server returns a synthetic /auth/me with + // 200 so this branch never fires; flipping AUTH_ENABLED=true is what + // activates the redirect end-to-end. + if (res.status === 401 && !location.pathname.endsWith('/login.html')) { + location.replace('/login.html'); + throw new Error('Unauthenticated — redirecting to login'); + } if (!res.ok) throw new Error(res.status + ' ' + res.statusText); return res.json(); }