fix(auth): self-hoster opt-out for session-cookie Secure flag (v1.5.1)

v1.5.1 patch. Fixes the silent login failure self-hosters hit on
plain-HTTP origins that aren't localhost — the bundled Docker image
runs NODE_ENV=production, so every cookie carried Secure, and modern
browsers drop those on http://10.x.x.x style origins. The session
cookie never made it back to the browser, /api/auth/me returned 401,
and the login page reloaded.

New SESSION_COOKIE_SECURE env var with three values:
- unset (default) — flag follows NODE_ENV === "production" (unchanged)
- "false" — never set (LAN / VPN / Tailscale-only opt-out)
- "true"  — always set (pnpm dev fronted by HTTPS)

Shared helper at src/lib/auth/secure-cookie.ts replaces four scattered
literals across session, codex device-start, and withings connect.
Six unit cases pin the decision matrix.

OpenAPI spec generator now reads info.version from package.json so
the drift gate stays meaningful instead of catching every release bump.

Default behaviour unchanged for HTTPS-fronted deployments.
This commit is contained in:
Marc-André Bombeck authored and GitHub committed 2026-05-26 19:16:44 +02:00
1 parent 1cd28acebc
commit c6d2b212f2
11 files changed
+161 -8

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: HealthLog API
version: 1.5.0
version: 1.5.1
description: >-
Self-hosted personal-health-tracking PWA — public API surface for the iOS native client and external ingest.
+34
View File
@@ -218,3 +218,37 @@ than the actual proxy hop count, or the proxy is dropping the
The one-shot stderr warning `[getClientIp] TRUST_PROXY_HOPS=N but
X-Forwarded-For carried M entries` confirms a mismatch. Adjust the
env var down to the actual chain length and restart.
## Running over plain HTTP on a LAN (no reverse proxy)
The session cookie carries the `Secure` flag by default under
`NODE_ENV=production`, which is what the bundled Docker image runs.
On a plain-HTTP origin that is not `localhost` / `127.0.0.1` / `::1`,
modern browsers silently drop a `Secure` cookie before sending the
next request — the login round-trip looks like it succeeds (the
server returns 200 + `Set-Cookie`) but the very next page load
401s and bounces back to `/auth/login`.
If you genuinely want to serve HealthLog on `http://10.0.0.42:3000`
or `http://healthlog.lan:3000` (NAS, homelab, Tailscale-only
surface) and trust the network the traffic crosses, set:
```
SESSION_COOKIE_SECURE=false
```
in your `.env`. The session, onboarding-hint, Withings OAuth state,
and Codex device-OAuth state cookies all stop emitting `Secure` and
login works over HTTP. The trade-off is explicit: the session
cookie crosses the wire unencrypted, so the network it traverses
needs to be one you control.
**Do not set this on a deployment that ever serves plain HTTP to the
open internet.** Anyone with passive network access can capture the
session cookie and impersonate the user. The right answer for any
public-facing host is the reverse proxy + Let's Encrypt path
documented above.
The default behaviour is unchanged when this variable is unset —
deployments behind an HTTPS-terminating proxy keep setting `Secure`
exactly as before.