docs(02-01): complete auth-substrate plan
This commit is contained in:
parent
2c84f4275b
commit
fb9aac30ba
4 changed files with 149 additions and 17 deletions
|
|
@ -17,8 +17,8 @@ Requirements for the initial Go+HTMX milestone. Each maps to exactly one roadmap
|
|||
|
||||
### Authentication
|
||||
|
||||
- [ ] **AUTH-01**: User can sign up with email and password (server-side validation, bcrypt/argon2 hash)
|
||||
- [ ] **AUTH-02**: User can log in with email and password and receives a server-managed session
|
||||
- [x] **AUTH-01**: User can sign up with email and password (server-side validation, bcrypt/argon2 hash)
|
||||
- [x] **AUTH-02**: User can log in with email and password and receives a server-managed session
|
||||
- [ ] **AUTH-03**: Sessions persist via HTTP-only, signed cookies (Secure + SameSite=Lax) and survive browser refresh
|
||||
- [ ] **AUTH-04**: User can log out from any authenticated page (server invalidates session)
|
||||
- [ ] **AUTH-05**: Protected routes redirect unauthenticated requests to the login page; authenticated users on auth pages are sent to the dashboard
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
| # | Phase | Goal | Requirements |
|
||||
|---|-------|------|--------------|
|
||||
| 1 | Foundation | Fresh `backend/` Go package boots, renders HTMX, talks to Postgres | FOUND-01..05 |
|
||||
| 2 | Auth | A user can sign up, log in, and stay logged in | AUTH-01..07 |
|
||||
| 2 | 1/7 | In Progress| |
|
||||
| 3 | Tablos CRUD | An authenticated user can manage their tablos end-to-end | TABLO-01..06 |
|
||||
| 4 | Tasks (Kanban) | A user can run a kanban board inside a tablo | TASK-01..07 |
|
||||
| 5 | Files | A user can attach, list, download, delete files on a tablo | FILE-01..06 |
|
||||
|
|
@ -58,9 +58,9 @@ Plans:
|
|||
|
||||
**User-in-loop:** Approve the `users` and `sessions` table schemas (columns, indexes, deletion semantics) before sqlc generation. Approve hash algorithm choice.
|
||||
|
||||
**Plans:** 7 plans
|
||||
**Plans:** 1/7 plans executed
|
||||
Plans:
|
||||
- [ ] 02-01-PLAN.md — Schema + sqlc + auth-package skeleton (citext + users + sessions, test DB harness)
|
||||
- [x] 02-01-PLAN.md — Schema + sqlc + auth-package skeleton (citext + users + sessions, test DB harness)
|
||||
- [ ] 02-02-PLAN.md — argon2id password hashing (TDD: Hash/Verify with PHC encoding)
|
||||
- [ ] 02-03-PLAN.md — Session store + cookie + ResolveSession/RequireAuth/RedirectIfAuthed middleware
|
||||
- [ ] 02-04-PLAN.md — Signup vertical slice (form → validate → hash → InsertUser → session → cookie → redirect)
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
gsd_state_version: 1.0
|
||||
milestone: v1.0
|
||||
milestone_name: milestone
|
||||
status: ready_to_plan
|
||||
last_updated: "2026-05-14T19:44:02.762Z"
|
||||
status: in_progress
|
||||
last_updated: "2026-05-14T21:57:00.000Z"
|
||||
progress:
|
||||
total_phases: 7
|
||||
completed_phases: 1
|
||||
total_plans: 11
|
||||
completed_plans: 4
|
||||
percent: 36
|
||||
total_plans: 18
|
||||
completed_plans: 5
|
||||
percent: 28
|
||||
---
|
||||
|
||||
# STATE
|
||||
|
|
@ -23,14 +23,14 @@ progress:
|
|||
See: `.planning/PROJECT.md` (updated 2026-05-14)
|
||||
|
||||
**Core value:** A user can sign in and run the Tablos workflow — create tablos, manage their tasks (kanban), and attach files — without a JS framework.
|
||||
**Current focus:** Phase 01 — foundation
|
||||
**Current focus:** Phase 02 — authentication, Plan 02 next
|
||||
|
||||
## Phase Status
|
||||
|
||||
| # | Phase | Status |
|
||||
|---|-------|--------|
|
||||
| 1 | Foundation | ○ Pending |
|
||||
| 2 | Authentication | ○ Pending |
|
||||
| 1 | Foundation | ✓ Complete |
|
||||
| 2 | Authentication | ◑ In Progress (1/7 plans done) |
|
||||
| 3 | Tablos CRUD | ○ Pending |
|
||||
| 4 | Tasks (Kanban) | ○ Pending |
|
||||
| 5 | Files | ○ Pending |
|
||||
|
|
@ -39,15 +39,27 @@ See: `.planning/PROJECT.md` (updated 2026-05-14)
|
|||
|
||||
## Active Phase
|
||||
|
||||
**Phase 1: Foundation** — not started.
|
||||
**Phase 2: Authentication** — Plan 01 complete. Plan 02 (password hashing) next.
|
||||
|
||||
Next: `/gsd-discuss-phase 1` to gather context, or `/gsd-plan-phase 1` to plan directly.
|
||||
## Decisions
|
||||
|
||||
- **Consolidated internal/auth package** (not split with internal/session) — RESEARCH Open Question 3
|
||||
- **compose Postgres + schema isolation for tests** (not testcontainers-go) — RESEARCH Open Question 1
|
||||
- **goose.SetTableName per test-schema** prevents public goose_db_version table collision
|
||||
- **argon2id** over bcrypt for password hashing — D-08, confirmed by user
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
| Phase | Plan | Duration | Tasks | Files |
|
||||
|-------|------|----------|-------|-------|
|
||||
| 02-authentication | 01 | ~10min | 3 | 9 |
|
||||
|
||||
## Notes
|
||||
|
||||
- Existing `go-backend/` is set aside; new code lives in a fresh `backend/` Go package.
|
||||
- DB schema is changing from the JS/Supabase version — user is in the loop on every schema decision (Phases 2–5).
|
||||
- GSD subagents are not installed in this repo; downstream commands may fail until `npx get-shit-done-cc@latest --global` is run.
|
||||
- Phase 2 Plan 01 SUMMARY: `.planning/phases/02-authentication/02-01-SUMMARY.md`
|
||||
- Commits: 513044d (migration), 799c260 (sqlc), 2c84f42 (auth package + test harness)
|
||||
|
||||
---
|
||||
*Last updated: 2026-05-14 after project initialization*
|
||||
*Last updated: 2026-05-14 after 02-01 execution*
|
||||
|
|
|
|||
120
.planning/phases/02-authentication/02-01-SUMMARY.md
Normal file
120
.planning/phases/02-authentication/02-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
---
|
||||
phase: 02-authentication
|
||||
plan: "01"
|
||||
subsystem: backend/auth-substrate
|
||||
tags: [go, postgres, sqlc, migration, auth-foundation, goose, citext, uuid]
|
||||
dependency_graph:
|
||||
requires: [01-foundation]
|
||||
provides: [auth-migration, sqlc-bindings, auth-package-skeleton, test-harness]
|
||||
affects: [02-02, 02-03, 02-04, 02-05, 02-06, 02-07]
|
||||
tech_stack:
|
||||
added:
|
||||
- github.com/pressly/goose/v3 v3.27.1 (test harness — per-schema migration isolation)
|
||||
patterns:
|
||||
- per-test isolated schema via unique schemaName + schema-scoped DSN
|
||||
- goose.SetTableName for per-test version table isolation (no public-schema collision)
|
||||
- sqlc citext→string + uuid→uuid.UUID overrides (Pattern 10)
|
||||
key_files:
|
||||
created:
|
||||
- backend/migrations/0002_auth.sql
|
||||
- backend/internal/db/queries/users.sql
|
||||
- backend/internal/db/queries/sessions.sql
|
||||
- backend/internal/auth/doc.go
|
||||
- backend/internal/auth/types.go
|
||||
- backend/internal/auth/testdb_test.go
|
||||
modified:
|
||||
- backend/sqlc.yaml (added overrides block)
|
||||
- backend/.env.example (added TEST_DATABASE_URL + SESSION_SECRET)
|
||||
- backend/go.mod + go.sum (added pressly/goose/v3)
|
||||
decisions:
|
||||
- "Consolidated internal/auth package (not split with internal/session) — RESEARCH Open Question 3 resolved"
|
||||
- "Test harness uses compose Postgres + schema isolation (not testcontainers-go) — RESEARCH Open Question 1 resolved"
|
||||
- "goose.SetTableName per test-schema prevents collision with public goose_db_version table"
|
||||
- "goose v3.27.1 pinned to match justfile CLI version"
|
||||
- "schemaName uses UUID[:12] with hyphens replaced by underscores for valid SQL identifiers"
|
||||
metrics:
|
||||
duration: "~10 minutes"
|
||||
completed_date: "2026-05-14"
|
||||
tasks_completed: 3
|
||||
tasks_total: 3
|
||||
files_created: 6
|
||||
files_modified: 3
|
||||
---
|
||||
|
||||
# Phase 2 Plan 01: Auth Substrate (Migration + sqlc + Package Skeleton) Summary
|
||||
|
||||
**One-liner:** Postgres migration with citext users+sessions tables, sqlc type-safe bindings with citext→string and uuid→uuid.UUID overrides, and internal/auth package with types/constants/sentinel-errors plus a per-test-schema Postgres harness.
|
||||
|
||||
## What Was Built
|
||||
|
||||
### Task 1: Migration 0002_auth.sql
|
||||
Created `backend/migrations/0002_auth.sql` with goose Up/Down annotations:
|
||||
- `CREATE EXTENSION IF NOT EXISTS citext` + `pgcrypto` (defensive for gen_random_uuid)
|
||||
- `users` table: id uuid PK, email citext UNIQUE, password_hash text, created_at/updated_at timestamptz (D-01)
|
||||
- `sessions` table: id text PK (SHA-256 hex per D-05), user_id FK ON DELETE CASCADE, created_at/expires_at (D-04)
|
||||
- Indexes: `sessions_user_id_idx` and `sessions_expires_at_idx`
|
||||
- No deleted_at, email_verified_at, user_agent, ip_address, last_seen_at (D-02, D-03, D-04)
|
||||
- Up/Down round-trip verified against compose Postgres (goose Up → Down → Up exits 0)
|
||||
|
||||
### Task 2: sqlc.yaml overrides + query files + generated bindings
|
||||
- `sqlc.yaml`: added `overrides` block mapping `citext→string` and `uuid→uuid.UUID` (Pattern 10, Pitfall 3)
|
||||
- `users.sql`: InsertUser :one + GetUserByEmail :one
|
||||
- `sessions.sql`: InsertSession :exec, GetSessionWithUser :one (with `expires_at > now()` per D-07), DeleteSession :exec, DeleteSessionsByUser :exec, ExtendSession :exec
|
||||
- `sqlc generate` produces `Email string` (not pgtype.Text) and `uuid.UUID` (not pgtype.UUID) — citext and uuid overrides verified
|
||||
- Generated files are gitignored (per Phase 1 .gitignore) and regenerated by `sqlc generate`
|
||||
|
||||
### Task 3: internal/auth package skeleton + test harness + env docs
|
||||
- `auth/doc.go`: package comment — consolidated layout decision documented
|
||||
- `auth/types.go`: User struct, Session struct, SessionCookieName (D-12), SessionTTL 30d (D-09), SessionExtendThreshold 7d (D-09), ErrSessionNotFound, ErrInvalidHash, ErrIncompatibleVersion
|
||||
- `auth/testdb_test.go`: `setupTestDB(t)` creates unique schema (test_\<uuid12\>), runs goose Up with schema-specific version table via `goose.SetTableName`, returns pool + cleanup. `TestSetupTestDB_Roundtrip` pings pool and verifies users table visibility. Test skips when neither TEST_DATABASE_URL nor DATABASE_URL is set.
|
||||
- `.env.example`: added `TEST_DATABASE_URL` and `SESSION_SECRET` with generation instructions
|
||||
- `go.mod`: added `github.com/pressly/goose/v3 v3.27.1` as direct dependency
|
||||
|
||||
## Decisions Made
|
||||
|
||||
1. **Consolidated internal/auth package** (not split with internal/session): all auth capabilities (password, session, ratelimit, cookie, csrf) live in one package. Phase 1's `internal/session/doc.go` placeholder is kept as-is (one-liner pointing at `internal/auth`). See RESEARCH Open Question 3.
|
||||
|
||||
2. **compose Postgres + schema isolation** (not testcontainers-go): reuses the existing compose Postgres via `TEST_DATABASE_URL`. Per-test isolation via unique schemas avoids testcontainers podman friction (RESEARCH Open Question 1, Assumption A7).
|
||||
|
||||
3. **goose.SetTableName per test**: each test schema gets its own goose version table (`test_<uuid>_goose_version`) so the public `goose_db_version` (tracking production migration state) is never read/modified during test runs.
|
||||
|
||||
4. **UUID hyphens stripped from schema/table names**: `uuid.New().String()[:12]` can contain hyphens which are invalid in unquoted SQL identifiers; replaced with underscores.
|
||||
|
||||
5. **goose v3.27.1 pinned**: matches the `goose_version` in `backend/justfile` CLI installation.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written. All acceptance criteria verified.
|
||||
|
||||
## Threat Flags
|
||||
|
||||
No new security-relevant surface introduced. This plan only creates:
|
||||
- Schema migration (DDL only — no network endpoints)
|
||||
- sqlc query files and generated bindings (parameterized queries prevent SQL injection — T-2-06 defense)
|
||||
- Internal Go package with types and test helpers
|
||||
|
||||
T-2-06 mitigated: sessions.id column is designed for SHA-256(token) hex per D-05; sqlc query `GetSessionWithUser` includes `AND expires_at > now()` gate (D-07 lazy expiry).
|
||||
T-2-11 mitigated: goose Up/Down round-trip exercised and exits 0.
|
||||
T-2-12 mitigated: `.env.example` SESSION_SECRET has empty placeholder value only; real value comes from `.env` (gitignored since Phase 1).
|
||||
|
||||
## Commits
|
||||
|
||||
| Task | Commit | Description |
|
||||
|------|--------|-------------|
|
||||
| 1 | 513044d | feat(02-01): add 0002_auth.sql migration |
|
||||
| 2 | 799c260 | feat(02-01): add sqlc queries + citext/uuid overrides |
|
||||
| 3 | 2c84f42 | feat(02-01): create internal/auth package skeleton, test DB harness, env docs |
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
Files verified:
|
||||
- backend/migrations/0002_auth.sql ✓
|
||||
- backend/internal/db/queries/users.sql ✓
|
||||
- backend/internal/db/queries/sessions.sql ✓
|
||||
- backend/internal/auth/doc.go ✓
|
||||
- backend/internal/auth/types.go ✓
|
||||
- backend/internal/auth/testdb_test.go ✓
|
||||
- backend/sqlc.yaml ✓
|
||||
- backend/.env.example ✓
|
||||
|
||||
Commits verified: 513044d, 799c260, 2c84f42 all present in git log.
|
||||
Loading…
Reference in a new issue