132 lines
7 KiB
Markdown
132 lines
7 KiB
Markdown
# Auth simplification — deployment notes
|
|
|
|
The code-based login flow (email → 6-digit code → session) has been
|
|
replaced by a pure email-based lookup against the invite list. This is a
|
|
one-way change — after deploy, the server no longer reads `CODE_PEPPER`,
|
|
no longer talks to SMTP, and the `codes` table is dropped on first boot.
|
|
|
|
Invites now carry an optional first name, used on the welcome screen:
|
|
*"Thanks for your interest, Erik."* (or *"Thank you for your interest."*
|
|
when no name is on file).
|
|
|
|
## Files changed
|
|
|
|
### Replace (drop in over the existing ones)
|
|
|
|
| File | What changed |
|
|
|---|---|
|
|
| `server.js` | Drops `initMail()` call, drops the `CODE_PEPPER` fatal check at boot. |
|
|
| `src/db.js` | Adds `first_name` column to `invites` (idempotent migration). Drops the `codes` table + its index on startup (idempotent). Updates prepared statements — `getInvite` and `listInvites` return `first_name`; `upsertInvite` takes it as an argument; `deleteInvite` is unchanged. Removes all `codes`-related statements. Cleanup sweep no longer touches `codes`. |
|
|
| `src/auth.js` | `POST /auth/request-code` and `POST /auth/verify-code` removed. Single `POST /auth/login` endpoint — returns `200 {ok, firstName}` on success, `403 {error: "not_invited"}` otherwise. Rate limit: 30 per IP per hour. `GET /auth/me` now returns `{email, firstName}`. |
|
|
| `src/sessions.js` | Removes `CODE_TTL_MS`, `randomCode`, `hashCode`, `constantTimeEqual`. Keeps `SESSION_TTL_MS`, `COOKIE_NAME`, `randomSessionId`, `issueSession`, `clearSession`, `currentSession`. |
|
|
| `bin/invite.js` | `add` accepts an optional first name as a third argument: `node bin/invite.js add erik@example.com Erik`. `list` shows the name in square brackets next to each entry. `remove` unchanged. Re-running `add` on an existing email updates the first name only — `invited_at` and `invited_by` are preserved. |
|
|
| `public/entrance.html` | Removes the six code-digit input block and all associated styles (`.code-row`, `.code-cell`, `.quiet`, "use a different email"). Removes step 3's duplicate welcome title markup — the welcome title is now set by JS on step-enter. |
|
|
| `public/entrance.js` | Two-step state machine (email → welcome) instead of three. On submit: `POST /auth/login`. Success → set welcome title, advance. `403` → inline "not invited" message in-place. Removes `submitCode()`, code-cell handling, paste/arrow-key logic, the "use different email" button, and the `rememberedEmail` / `submitting` state. |
|
|
| `package.json` | Removes `nodemailer` dependency. Bumps version to `0.2.0`. |
|
|
| `.env.example` | Removes `CODE_PEPPER`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASS`, `MAIL_FROM`. Keeps `PORT`, `PUBLIC_ORIGIN`, `NODE_ENV`. |
|
|
|
|
### Delete
|
|
|
|
| File | Why |
|
|
|---|---|
|
|
| `src/mail.js` | SMTP transport and `sendCode()` — no longer used. |
|
|
|
|
### Update (minor — you do this yourself)
|
|
|
|
| File | Action |
|
|
|---|---|
|
|
| `.env` (on the VPS, at `/etc/fenja/env`) | Remove `CODE_PEPPER=…`, `SMTP_HOST=…`, `SMTP_PORT=…`, `SMTP_USER=…`, `SMTP_PASS=…`, `MAIL_FROM=…`. Keep the rest. |
|
|
| `.env` (on your laptop) | Same. |
|
|
|
|
## Doc updates
|
|
|
|
I've not touched these — update them when you next edit them:
|
|
- `PROJECT.md` — "How auth works" section, "Non-negotiable properties" (drop CODE_PEPPER and SMTP)
|
|
- `INSTALL.md` — step 5 (env file), step 0 prerequisites (SMTP relay no longer needed)
|
|
- `OPERATIONS.md` — "Editing secrets" section
|
|
- `CHECKLIST.md` — section C (email + code form)
|
|
- `CLAUDE.md` — references to `CODE_PEPPER`, SMTP, `codes` table, `/auth/request-code`, `/auth/verify-code`
|
|
|
|
## Deploy sequence
|
|
|
|
Local first:
|
|
|
|
```powershell
|
|
# 1. Replace the files (see the list above). Delete src/mail.js.
|
|
|
|
# 2. Update your local .env — remove CODE_PEPPER, SMTP_*, MAIL_FROM lines.
|
|
|
|
# 3. Clean up node_modules to drop nodemailer:
|
|
npm install
|
|
|
|
# 4. Add first names to existing invites, or invite fresh with names:
|
|
node bin/invite.js add you@yourdomain.com Erik
|
|
|
|
# 5. Walk the flow:
|
|
npm run dev
|
|
# → http://127.0.0.1:3000
|
|
# → type your email → should land on welcome with personalised title
|
|
# → click "Learn more" → timeline
|
|
# → open a private window, type an unknown email → "not on invite list"
|
|
```
|
|
|
|
Then the VPS (once you're happy):
|
|
|
|
```bash
|
|
# On your laptop — rsync with the usual excludes:
|
|
rsync -avz --delete \
|
|
--exclude node_modules --exclude data --exclude .env --exclude .git \
|
|
./ user@project-bifrost.fenja.ai:/tmp/fenja-upload/
|
|
|
|
# On the VPS:
|
|
sudo rsync -a --delete /tmp/fenja-upload/ /opt/fenja/
|
|
sudo rm -f /opt/fenja/src/mail.js # make sure it's gone after rsync
|
|
sudo chown -R fenja:fenja /opt/fenja
|
|
rm -rf /tmp/fenja-upload
|
|
|
|
cd /opt/fenja
|
|
sudo -u fenja npm ci --omit=dev # drops nodemailer from node_modules
|
|
|
|
# Edit the VPS .env to remove CODE_PEPPER and SMTP_*:
|
|
sudo nano /etc/fenja/env
|
|
|
|
sudo systemctl restart fenja
|
|
sudo journalctl -u fenja -n 20
|
|
# Expect: just "[bifrost] listening on 127.0.0.1:3000"
|
|
# No more "[mail] SMTP relay reachable" line — the whole SMTP stack is gone.
|
|
```
|
|
|
|
First time the server boots, the migration runs automatically:
|
|
- adds `first_name` column to `invites`
|
|
- drops the `codes` table
|
|
|
|
Both are idempotent — if you roll back and deploy again it's a no-op.
|
|
|
|
## Spot-check after deploy
|
|
|
|
- [ ] `curl -I https://project-bifrost.fenja.ai/` still returns 200 with all security headers.
|
|
- [ ] A non-existent session cookie → entrance page with the email field.
|
|
- [ ] Type an invited email → advances to welcome with *"Thanks for your interest, [Name]."* if the invite has a name, or *"Thank you for your interest."* if not.
|
|
- [ ] Type an uninvited email → inline red "This email is not on the invite list." on the email input.
|
|
- [ ] Click "Learn more about Project Bifrost" → `/timeline`.
|
|
- [ ] Refresh `/` while logged in → land directly on the welcome step with the personalised title (served from `/auth/me`).
|
|
- [ ] `node bin/invite.js list` on the VPS shows the existing `quka93@gmail.com` with no name in square brackets (NULL is fine — legacy row).
|
|
- [ ] `node bin/invite.js add someone@example.com Alice` shows `Invited someone@example.com (Alice)`.
|
|
- [ ] DevTools network tab: no 404s, no CSP violations.
|
|
|
|
## Rollback, if needed
|
|
|
|
The migration is not trivially reversible — `codes` was dropped. If you
|
|
need to roll back, restore from the nightly SQLite backup at
|
|
`/opt/fenja/data/backup-YYYY-MM-DD.sqlite`, and redeploy the previous
|
|
code. Invites with `first_name` values stay in the backup but will be
|
|
ignored by the old `src/db.js` that doesn't select that column — no harm
|
|
done.
|
|
|
|
## What I deliberately did NOT do
|
|
|
|
- Didn't remove the `rate_limits` table — still used by the login endpoint.
|
|
- Didn't change `src/middleware.js` — `rateLimit()` and `requireAuth()` behaviour is unchanged.
|
|
- Didn't change anything inside `protected/` — the timeline, archive, and Project Bifrost scenes are all untouched.
|
|
- Didn't change Nginx config or the systemd service unit — no operational changes.
|
|
- Didn't add an "admin web UI" or `/auth/invite` HTTP endpoint — invites still happen only via the CLI, as before.
|