Files
mercadodevida/specs/F-005-identity/SPEC.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.7 KiB

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

Problem

Customers need accounts; nothing trusts who calls the API.

Goal

Registration, login, logout with hashed passwords and secure sessions — server is the only authority for identity (never trust the frontend).

Scope IN

  • src/modules/identity with hexagonal layers: domain / application / infrastructure / api
  • Register, login, logout use cases over PostgreSQL (source of truth)
  • Argon2id password hashing (OWASP parameters)
  • Server-side opaque sessions stored in DB; cookie carries a random token, DB stores only its SHA-256 hash
  • Secure session cookie: HttpOnly, Secure, SameSite
  • Login rate limiting (10 consecutive failures -> 429 with cooldown) behind an interface
  • Migration 002_identity (identity_users, identity_sessions) with down
  • Config: COOKIE_SECURE flag (default true)

Scope OUT

  • No MFA, no OAuth providers, no profile editing
  • No email verification flow, no password reset
  • No distributed rate limiting (in-memory per instance; documented)
  • No expired-session sweeper (follow-up)

Acceptance criteria

  1. Given valid credentials When login Then HTTP 200 and secure session cookie set.
  2. Given wrong password When login Then HTTP 401 and no user enumeration hint (unknown email returns the identical 401).
  3. Passwords stored with argon2, never plaintext or reversible.
  4. Given 10 failed logins in a row When next login attempted Then HTTP 429.
  5. Session cookie is HttpOnly, Secure and SameSite.
  6. ./scripts/verify.sh green.

Dependencies added

  • argon2 (canonical Argon2id implementation; PHC string output)
  • @fastify/cookie (explicit setCookie/clearCookie; official Fastify plugin) Justification goes to spec/tech.md.