Files
mercadodevida/work/artifacts/F-005/architect.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

1.9 KiB

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

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

Deliverables

  • specs/F-005-identity/SPEC.md, DESIGN.md, TASKS.md, TESTS.md

Key decisions

  1. Server-side opaque sessions in PostgreSQL, never JWT/client-trusted tokens: the cookie carries 512 bits of randomness; the DB stores only the SHA-256 hash. A DB leak yields no usable sessions; logout is a real revocation.
  2. Hexagonal identity module (domain/application/infrastructure/api) as the ticket demands. Domain knows no framework; ports live in domain; infra implements them. This module is the template for catalog/inventory/orders later.
  3. argon2id with OWASP 2024 params (m=19456 KiB, t=2, p=1) behind a PasswordHasher port. Timing equalization via dummy-hash verify on unknown email.
  4. Rate limiting behind a LoginRateLimiter interface, in-memory implementation for the single-instance monolith: 10 consecutive failures per email, 15-min cooldown, reset on success, Retry-After on 429. Redis-backed swap later without touching use cases.
  5. Cookie policy: HttpOnly + SameSite=Lax always; Secure via COOKIE_SECURE (default true — production-safe; .env.example sets false for local http).
  6. Foundation touchpoints kept minimal: createPool(connectionString), COOKIE_SECURE in config, buildApp gains optional pool dep (foundation-only app preserved when absent).

Security posture

  • No plaintext or reversible passwords anywhere (argon2 PHC strings only).
  • Generic 401 for both unknown email and wrong password (no enumeration).
  • Parameterized SQL only; citext unique email enforced in DB (race-safe).
  • Session expiry + revocation enforced in the DB query, not in app memory.

Risks

  • argon2 native build (toolchain verified present; fallback @node-rs/argon2 behind port).
  • In-memory limiter is per-instance (documented; acceptable for monolith slice).