feat(web): house-password login on the LAN - #915
Conversation
Session cookie after POST /api/auth/login. Settings turns api.lan_auth on and off. Live status stays visible until a write needs the password.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e95b2e9ea3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (!r.ok) throw new Error(body.error || "enable failed"); | ||
| if (pwEl) pwEl.value = ""; | ||
| if (cfEl) cfEl.value = ""; | ||
| setLanMsg("LAN password is on"); | ||
| refreshLanAuth(); |
There was a problem hiding this comment.
Synchronize the settings snapshot after toggling LAN auth
After either toggle succeeds, the settings shell's currentConfig.api.lan_auth still contains the value loaded when the modal opened. Because the global Save button later replaces the entire config via POST /api/config, enabling and then saving silently turns authentication back off; worse, disabling and then saving sends the stale true value after the password was deleted, re-enabling LAN auth with no usable password and locking non-loopback users out. Update the shared config snapshot in both success paths (or reload it) before allowing a normal settings save.
Useful? React with 👍 / 👎.
miravoss26
left a comment
There was a problem hiding this comment.
Companion frontend PR to #914. POST /api/auth/login sets an HttpOnly session cookie (12h, SameSite=Strict, no Secure since the LAN is plain HTTP), /api/auth/logout drops it, Settings → System toggles the password, and a fetch wrapper opens a login modal on the specific 401.
What checks out:
- Session tokens are 32 bytes from
crypto/rand, hex-encoded, in-memory only (restart logs everyone out, as documented). - Cookie flags are correct for the environment: HttpOnly + SameSite=Strict + no Secure, and there's a test that explicitly asserts Secure is never set.
- Bearer wins over cookie when both are present (tested).
- Password rotation and disable both call
dropAllLANSessions(), so an old session can't outlive a password change. - CSRF is covered twice over: SameSite=Strict on the cookie, plus the existing Origin/Sec-Fetch-Site check in
Authenticate(). - The JS side only intercepts same-origin
/api/401s carrying the exact "valid LAN password required" message, and there's a test confirming the password never toucheslocalStorage/sessionStorage.
One thing I can't verify from here: no CI has run on this PR at all. gh pr checks 915 comes back empty and the commit status is pending with zero check-runs. The PR body says the author ran the Go and JS suites locally, but there's no CI confirmation on this exact head. It's stacked on #914's branch rather than master, which may be why.
Code read is solid, same quality as #914. I'd want to see CI actually run green before calling this a clean merge, and #914 needs to land first since this branches off it.
Summary
Same change as #913, restacked onto #914.
#913 still sits on the old #912 tip. This PR has the login/cookie/Settings work on top of the rebased LAN-auth seam.
POST /api/auth/loginsets an HttpOnlyftw_lancookie (12 h, SameSite=Strict, not Secure)POST /api/auth/logoutdrops the session/api/auth/password401 valid LAN password requiredlocalStorageLive status, energy and prices stay visible without the password.
Stack
Sits on #914 (
agent/lan-auth-seam-rebased) →master. Merge #914 first.Replaces
#913.
Test plan
go test ./go/internal/api/node --test web/lan-auth.test.mjs web/javascript-syntax.test.mjscurlwith Bearer still worksA human should look at Settings → System and the login modal (CODEOWNERS).