ADR-0002: Authentication and Password Hashing
- Status: Accepted
- Date: 2024-08-16
Context
The Conduit API requires user authentication for most write operations and for retrieving the current user. The API is a stateless HTTP service that may be horizontally scaled, so authentication state must not be stored in server memory. We also need to store user passwords securely, resistant to offline brute-force and GPU-accelerated cracking attacks.
Decision
- JWT (JSON Web Tokens) for stateless authentication. On successful registration or login the service issues a signed JWT containing the user id. Clients present it via the
Authorization: Token <jwt>header (per the Conduit spec). Tokens are verified on each protected request; no server-side session store is required, which keeps the service stateless and scalable. The signing secret comes fromJWT_SECRETand expiry fromJWT_EXPIRY. - argon2 for password hashing. Argon2id is a modern, memory-hard password hashing algorithm and the winner of the Password Hashing Competition. It is the current OWASP-recommended default for new applications.
Alternatives Considered
- bcrypt was rejected. Its common Node bindings pull in a native build toolchain through
node-pre-gyp, which has a history of transitive dependency CVEs (the node-pre-gyp / tar / minimist chain), complicatingnpm audit --audit-level=highgates. bcrypt also has a 72-byte password truncation quirk and is not memory-hard, making it weaker against GPU attacks than argon2id. - Server-side sessions (cookie + session store) were rejected because they require shared session state, adding infrastructure and coupling that the stateless JWT approach avoids.
- scrypt is a reasonable memory-hard alternative but argon2id is the more modern, competition-winning choice with a well-maintained Node binding.
Consequences
- Tokens are self-contained; revocation before expiry is not supported without additional infrastructure (acceptable for this scope). Expiry is kept short (
JWT_EXPIRY, default7d). - The
JWT_SECRETmust be a strong, secret value injected via environment configuration and validated at startup (fail fast if missing). - argon2 is used for both hashing on registration and verification on login; password hashes are never returned by the API.