Skip to content

feat(web): login gate for api.auth.mode - #859

Closed
Sanjin-Maker wants to merge 3 commits into
srcfl:masterfrom
Sanjin-Maker:agent/web-login
Closed

feat(web): login gate for api.auth.mode#859
Sanjin-Maker wants to merge 3 commits into
srcfl:masterfrom
Sanjin-Maker:agent/web-login

Conversation

@Sanjin-Maker

Copy link
Copy Markdown
Contributor

Summary

Stacked on #858 (auth enforcement) — review the last commit until the chain lands. @erikarenhill for /web/. Completes the commercial-readiness auth slice.

  • <ftw-login-gate> mounts first in <body>: asks GET /api/auth/session; on mode: open or an authenticated session it remove()s itself immediately — zero visual or behavioral change for every existing install. On login-required modes it covers the app with a themed sign-in form (shadow styles from :root tokens, both themes, no dependencies).
  • Successful login sets the HttpOnly session cookie server-side and reloads, so every component's fetches carry the session from the start. Uniform "wrong username or password" mirrors the API's no-oracle rule.
  • Unreachable backend does NOT block — the app's existing error states own that failure.
  • Decision + error-text logic is pure (login-math.js) under node --test.

Not yet screenshot-verified against a live required-mode stack; markup follows the house component patterns and the decision logic is unit-tested.

Verification

npm test: 260 pass (7 new). DCO signed; minor changeset.

🤖 Generated with Claude Code

Sanjin-Maker and others added 3 commits August 6, 2026 22:52
users table (operator/viewer, argon2id PHC hashes) in state, plus
go/internal/localauth: constant-time password verification at OWASP
argon2id parameters and in-memory bearer sessions with expiry and
per-user revocation. Sessions are memory-only on purpose — restart
logs everyone out, and no session secret touches the database. API
enforcement comes separately; nothing changes for existing installs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sanjin Naidu <sanjin@sanrowconsulting.com>
api.auth.mode open|local_trust|required layered outside the existing
SecureMutations gate: open is byte-identical to today, local_trust
gates only non-local requests (viewer reads, operator mutations,
bearer token still valid for automation), required gates everything
but login/health/static. ftw user CLI bootstraps accounts on the box;
startup refuses login modes with zero enabled operators. Every
mutation attempt lands in audit_log with its principal, served at
GET /api/audit.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sanjin Naidu <sanjin@sanrowconsulting.com>
Overlay asks /api/auth/session on load; open mode or a live session
removes it instantly so existing dashboards render untouched. Login-
required modes block the app with a themed sign-in form (uniform
wrong-credentials text mirroring the API's no-oracle rule); success
reloads so every component fetches with the session cookie.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Sanjin Naidu <sanjin@sanrowconsulting.com>
@frahlg

frahlg commented Aug 7, 2026

Copy link
Copy Markdown
Member

The gate does not appear for an unauthenticated user in the current stack. #858 protects GET /api/auth/session, so it returns 401. _check() treats every non-OK response as a reason to remove the gate, leaving the dashboard visible while its API calls fail.

Make session discovery return an unauthenticated payload, or handle 401 as login-required without hiding the gate. After rebasing onto the app API-boundary work now on master, this also needs a human browser check for open, local-trust, required, bad-password, expired-session, and unreachable-backend states.

@miravoss26 miravoss26 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the last commit (feat(web): login gate for api.auth.mode) per the stacked-PR note — the <ftw-login-gate> element, its pure decision logic (login-math.js), and the unit tests.

Correctness: shouldShowLogin/loginErrorText are small and match the design doc exactly (open/authenticated → never block, 401 → show, fetch failure → don't block and let the app's own error UI own it). The component logic reads right: checks /api/auth/session on connect, removes itself when no gate is needed, reloads on successful login so every other component picks up the session cookie from a clean state.

Security: clean — errors render via err.textContent, never innerHTML, so there's no XSS surface even though the error text technically comes from a server response. No dynamic values get interpolated into the template. SameSite=Strict on the session cookie (from #858) covers CSRF for the login POST reasonably well from this side.

Nothing to block on in this commit. Its actual security posture inherits directly from #858 underneath it, though — I flagged two real issues there (/api/audit unauthenticated in open mode, and a login timing side-channel that undercuts the "no username oracle" comment this file's loginErrorText also references). Worth keeping those linked so this PR isn't approved as if the chain below it is clean.

One nice-to-have, not a blocker: the password field isn't cleared after a failed attempt, so a shoulder-surfer gets a second read of it sitting in the input — minor UX/privacy nit, not worth holding the PR for.

frahlg commented Aug 8, 2026

Copy link
Copy Markdown
Member

Thank you @Sanjin-Maker for completing the prototype through the browser surface.

We are closing this branch with #857 and #858. The gate currently disappears on backend failure or 401 while the dashboard can remain visible and its API calls fail. Any future UI also requires human browser review for open, required, authenticated, unauthenticated, expired-session and backend-unavailable states.

The UI should come only after a written auth decision and an accepted API contract.

@frahlg frahlg closed this Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants