adding claude file and some plans

This commit is contained in:
2026-08-12 18:55:18 -06:00
parent b206964417
commit 4da16c7573
19 changed files with 1294 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# 05 — Auth, Sessions, OAuth & Tokens
Two credential paths, one identity: browser SPA uses **server-side sessions in HttpOnly cookies** (alexedwards/scs, Postgres store); scripts/CLI use **personal access tokens**. Local email/password and GitHub/Gitea OAuth all resolve to the same `users` row via `oauth_accounts`.
## Registration & login
- [ ] `POST /auth/register`: validate email format, username `[a-z0-9-_]{3,32}` lowercase-unique (citext), password ≥ 10 chars; bcrypt cost 12
- [ ] **First registered user gets `is_admin = true`** (instance bootstrap; ids 12 are reserved seeds, so the first human is id 3)
- [ ] `POST /auth/login`: verify bcrypt; on success `scs.RenewToken` (session fixation) then store user id; emit `auth.login_succeeded` / `auth.login_failed` (failed: no user enumeration in the response — generic message)
- [ ] Rate-limit register/login/password endpoints per IP (10/min)
- [ ] `POST /auth/logout`: destroy session
- [ ] Session config: Postgres store (`sessions` table), 30-day lifetime with idle-timeout 7 days, cookie `HttpOnly`, `SameSite=Lax`, `Secure` when BASE_URL is https
## OAuth (GitHub + Gitea)
- [ ] Standard authorization-code flow; `state` parameter stored in session, verified at callback (CSRF)
- [ ] GitHub: fixed endpoints; Gitea: endpoints derived from `GITEA_URL` (self-hosted friendly)
- [ ] Provider buttons hidden in the SPA when the corresponding env vars are unset (`/api/v1/me`-adjacent bootstrap config endpoint or injected at index render)
- [ ] Callback logic, in order:
- [ ] Existing `oauth_accounts(provider, provider_user_id)` row → log that user in
- [ ] Else: fetch the provider's **verified** primary email; if a user with that email exists → **link**: insert oauth_accounts row for that user, log in
- [ ] Else: create a new user (username derived from provider login, de-duplicated with numeric suffix; `password_hash` NULL) + oauth_accounts row, log in
- [ ] A linked user may later set a password via account settings (password change with no current password required only when `password_hash` IS NULL)
- [ ] Never auto-link on an **unverified** provider email (account-takeover vector) — fall through to create-new-user
## Personal access tokens
- [ ] Format `solopm_<40 chars base62>`; store SHA-256 hex in `token_hash`; show plaintext exactly once at creation
- [ ] `prefix` (first 12 chars) stored for display in the token list
- [ ] Bearer auth middleware: constant-time hash lookup; reject revoked/expired; update `last_used_at` (throttled to once/minute per token)
- [ ] Tokens act as the full user (no scopes in v1 — recorded in [17-decisions.md](17-decisions.md))
- [ ] Emit `auth.token_created` / `auth.token_revoked`
## Authorization model
- [ ] `is_admin` (system-wide): manage users, run cull/reindex, see admin endpoints. Admins are **not** implicit members of every project — they see only their own projects in normal UI (admin endpoints are separate)
- [ ] Project `owner`: everything a member can, plus: edit project settings, manage members/labels/webhooks, transfer ownership, delete project
- [ ] Project `member`: full read/write on the project's issues, epics, comments, wiki, attachments, links; manage own watches/favorites
- [ ] Non-member: **404** on all project-scoped routes
- [ ] System user (id 1) and zombie user (id 2): cannot log in (no password, no oauth rows, login explicitly rejects ids 12), never listed in pickers
## User deletion edge cases
- [ ] A user who is the sole owner of any project cannot be deleted (409 `conflict`, message lists the projects) — transfer ownership first
- [ ] Deletion is a soft delete; sessions destroyed and PATs revoked immediately
- [ ] At cull time the zombie user inherits authored records ([12-retention.md](12-retention.md)); audit_log keeps the original user id (immutable)
- [ ] Self-service account deletion follows the same rules as admin deletion