From 7e3e6b2a28f174236fae6344b930380ed700ddda Mon Sep 17 00:00:00 2001 From: Zac Gaetano Date: Wed, 27 May 2026 20:09:17 -0400 Subject: [PATCH] fix(auth): force HTTPS on dragonflight.live so login cookies stick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User reported infinite login loop on dragonflight.live. Root cause: openresty fronts both http:// and https:// without redirecting, and a user landing on http:// gets the Set-Cookie response silently dropped — cookies are Secure-only when TRUST_PROXY=true, and the CORS allowlist refuses the http:// origin. Result: login appears to succeed, next request has no session cookie, AuthGate bounces back to login. Two defensive layers (the openresty box is not in our reach): - web-ui index.html: tiny inline redirect; if location is http://dragonflight.live, rewrite to https:// before anything else runs. Bounded to that exact hostname so local / LAN access on http://172.18.91.x stays as-is. - mam-api: emit Strict-Transport-Security on HTTPS responses when AUTH_ENABLED=true. After one successful HTTPS visit, browsers auto-upgrade future http:// requests on their own — closes the loophole even if someone bypasses the index.html JS. Co-Authored-By: Claude Opus 4.7 --- services/mam-api/src/index.js | 12 ++++++++++++ services/web-ui/public/index.html | 8 ++++++++ 2 files changed, 20 insertions(+) diff --git a/services/mam-api/src/index.js b/services/mam-api/src/index.js index dad13ca..d7cf83f 100644 --- a/services/mam-api/src/index.js +++ b/services/mam-api/src/index.js @@ -62,6 +62,18 @@ app.use(express.json({ limit: '50mb' })); // Trust the reverse proxy only when explicitly told to (production HTTPS). if (process.env.TRUST_PROXY === 'true') app.set('trust proxy', 1); +// HSTS — once a browser has seen this header over HTTPS for dragonflight.live, +// it auto-upgrades every future http:// request to https:// before hitting the +// wire. Cookies are Secure-only (below) and the CORS allowlist rejects HTTP, +// so without HSTS a user who lands on http:// silently can't log in. +// Only emit on actual HTTPS responses; req.secure honors trust proxy + X-Forwarded-Proto. +if (process.env.AUTH_ENABLED === 'true') { + app.use((req, res, next) => { + if (req.secure) res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + next(); + }); +} + // Hard-fail when production-mode auth has no stable session secret. Without // this, express-session falls back to an in-memory random secret which // invalidates every session on restart and breaks multi-node deployments. diff --git a/services/web-ui/public/index.html b/services/web-ui/public/index.html index cab074c..bc933c4 100644 --- a/services/web-ui/public/index.html +++ b/services/web-ui/public/index.html @@ -2,6 +2,14 @@ + Dragonflight · Wild Dragon Broadcast