HVN Viewer Accounts
Step-by-step setup and acceptance testing for HVNid. Viewer accounts are completely separate from Command employee/admin access.
team_members role. HVN viewers authenticate through the public HVN account API and the hvn_users/hvn_user_sessions tables.What v20.4 now includes
- Register and sign in with email or username.
- Passwords stored as salted PBKDF2-SHA256 hashes; plaintext passwords are never stored.
__Host-hvn_sessionsession cookie with Secure, HttpOnly, SameSite=Lax and Path=/.- Profile, avatar, reminders, saved market/location and watchlist persistence in D1.
- Password change that invalidates other sessions.
- One-time, 30-minute password reset tokens stored only as SHA-256 hashes.
- Logout and permanent account deletion.
- Same-origin mutation checks and safe input/size limits.
Set it up in this exact order
Apply the current v20.4 schema
The same command-app/command-schema.sql now creates hvn_users, hvn_user_sessions and hvn_password_resets. Apply it to the D1 database used by staging. The schema is repeat-safe.
Bind D1 to the HVN Pages project
In the HVN Pages project, bind the same D1 database under the exact variable name COMMAND_DB. Redeploy the HVN project after changing the binding.
Deploy HVN with its Functions folder intact
The viewer endpoints are in hvn-site/functions/api/auth/, profile.js, location.js and watchlist.js. The HVN _routes.json sends /api/* through Pages Functions. Use Git integration or Wrangler; do not use dashboard drag-and-drop for a Pages Functions deployment.
Configure password-reset email
Add RESEND_API_KEY as a secret and HVN_RESET_FROM_EMAIL as a verified sender identity. Until both exist, password-reset requests intentionally return the same 503 response for known and unknown accounts instead of exposing whether an account exists.
Protect the public auth endpoints from abuse
Create Cloudflare rate-limiting/WAF rules for repeated requests to /api/auth/login, /api/auth/register and /api/auth/request-reset. Keep generic login/reset error messages; do not expose whether an email exists.
Test a real staging account
Register a disposable viewer. Sign out/in, edit profile, choose a market/location, add/remove a watchlist item, change password, and confirm every change survives a refresh and a second browser session.
Test password reset end to end
Request a reset email, open the actual link, create a new password, verify the link cannot be reused, and verify the old password no longer works.
Test deletion
Use a disposable account. Confirm a wrong password cannot delete it, a correct password does, the session cookie is cleared, and the viewer can no longer sign in.
Automated test
Run node TESTS/hvn-viewer-auth-integration.mjs, or simply ./TESTS/run-all.sh. The automated suite tests registration, hashed passwords, cookie flags, duplicate identity rejection, sessions, profile/location/watchlist persistence, cross-origin rejection, password change, expiry, reset, logout and deletion.
Fail-closed protection
Protected profile pages wait for the real /api/auth/me result. If account initialization fails, they redirect to sign-in instead of showing stale local profile data. Password-reset responses stay generic for known/unknown accounts, and a reset token is destroyed if the email provider fails before delivery.