Files
mercadodevida/work/artifacts/F-005/implementer.md
rikrdo 75293f39bc feat(identity): F-005 register/login/logout with argon2 sessions and rate limiting
- Hexagonal identity module: domain ports, use cases, argon2id hasher, pg repos
- Migration 002_identity: identity_users + identity_sessions (token hash only)
- Opaque 512-bit session tokens; DB stores SHA-256 hash; 7-day TTL in SQL
- Cookie HttpOnly + Secure (COOKIE_SECURE, default true) + SameSite=Lax
- LoginRateLimiter: 10 failures -> 429 + Retry-After, 15-min cooldown
- Anti-enumeration: identical generic 401 + dummy-hash timing equalization
- buildApp gains optional pool/cookieSecure; foundation-only app preserved
- 47 unit + 14 integration tests; live smoke covers all acceptance criteria
2026-08-14 22:58:32 +02:00

2.2 KiB
Raw Permalink Blame History

Implementer — F-005 Identity: register, login, sessions

done -> work/artifacts/F-005/implementer.md

Delivered

  • Migration migrations/002_identity.js: identity_users + identity_sessions (token_hash UNIQUE, FK ON DELETE CASCADE, user_id index). Reversible.
  • Hexagonal module src/modules/identity:
    • domain: user.ts, session.ts (7-day TTL), errors.ts, ports.ts
    • application: register-user.ts, login.ts, logout.ts, login-rate-limiter.ts
    • infrastructure: argon2-password-hasher.ts (argon2id m=19456 t=2 p=1), pg-user-repository.ts, pg-session-repository.ts, session-token.ts
    • api: identity.routes.ts (zod via parseJson, envelope v2 mapping)
  • Foundation wiring: createPool(connectionString), COOKIE_SECURE in config (default true), buildApp({ pool, cookieSecure }), server creates pool from config.

Deps added (justified in spec/tech.md)

  • argon2, @fastify/cookie

Evidence

  • npm run lint (prettier clean after format pass)
  • npm run lint:boundaries (39 files, R1/R2 OK — module tests stay in-subtree, DB integration tests live in src/app/tests)
  • npm run typecheck / npm run build
  • npm test: 46 passed, 14 skipped (integration)
  • npm run test:integration: 14/14 (register argon2-only storage, dup 409, invalid 400, cookie flags, identical 401 bodies, revocation + idempotent logout, 10 failures → 429 + Retry-After, foundation-only app keeps /auth 404)
  • Live smoke (curl): 201 register / 200 login + mdv_session …; HttpOnly; Secure; SameSite=Lax; Max-Age=604800 / 401 identical for wrong-password vs unknown-email / 204 logout clears cookie / 10×401 → 429 + retry-after: 900
  • ./scripts/verify.sh

Notes / deviations

  • F-002 migrations.itest baseline rollback test updated: down now uses count: 0 (full revert) since the suite has two migrations; test asserts identity tables AND app_meta are gone. Helper runMigrations gained an optional count param.
  • Rate limiter is in-memory per instance (documented in DESIGN risks; interface ready for Redis swap).
  • project/.env (gitignored) supplies TEST_DATABASE_URL to the test:integration script via node --env-file-if-exists=.env.