AcceptedDecided
ADR 0008: Argon2id for password hashing
bcrypt has been the safe default for password hashing for years and there are no known breaks of it, but the field has moved on.
On this page
- Status: Accepted
- Date: 2026-05-16
- Deciders: @jseifeddine
- Update (2026-05-28): the implementation moved from the
argon2package to@node-rs/argon2(a napi-rs binding to the Rustargon2crate - prebuilt binaries, no node-gyp at install time). The algorithm, parameters and decision are unchanged.@node-rs/argon2exports noneedsRehash, solib/auth/password.tsimplements its own by parsing the PHC string'sm,tandpparameters and comparing them to the current set.
Context#
bcrypt has been the safe default for password hashing for years and there are no known breaks of it, but the field has moved on. OWASP's current Password Storage Cheat Sheet recommends Argon2id as the primary password hashing function. We pick the recommended primary.
Decision#
Local-account password hashing uses Argon2id via the argon2 npm package. Parameters track
OWASP's current recommendations and are reviewed yearly.
Initial parameters (OWASP Cheat Sheet 2024):
- m_cost (memory):
19456(≈19 MiB) - t_cost (iterations):
2 - parallelism:
1 - hash length: 32 bytes
- salt length: 16 bytes (generated per-hash by the library)
Rationale#
- Memory-hard. Argon2id requires significant RAM per attempt, making large-scale GPU / ASIC attacks much more expensive than bcrypt's CPU-bound work. This is the property that matters as commodity hardware gets faster.
- OWASP's primary recommendation. Aligning with OWASP gives us a defensible position in any security audit.
- Hybrid mode. Argon2id combines Argon2i (side-channel resistant) and Argon2d (GPU-resistant). Best of both.
- Library quality. The
argon2npm package wraps the reference C implementation with reasonable defaults and active maintenance.
Alternatives considered#
- bcrypt. Still safe, but Argon2id has clearly surpassed it on the OWASP guidance.
- scrypt. Also memory-hard, also good. Rejected because Argon2id is the more recent design and has the OWASP endorsement.
- PBKDF2. Still common in regulated industries. Rejected - CPU-bound only, weaker against GPU attacks.
Consequences#
- API tokens use the same hashing scheme. A
pda_pat_...token is stored as an Argon2id hash with the same parameters. Verification cost is the same as a password - acceptable because API token verification is rate-limited at the route layer. - Verify cost is non-trivial. A typical verify takes 50–100ms on commodity hardware. Login endpoints absorb this without complaint. Bulk verification (e.g., importing users from another system) needs batching and a "this will take a while" UI.
- Parameter rotation. Every year, review OWASP. If we bump parameters, existing hashes
remain valid; users get re-hashed on next successful login (the
argon2.needsRehashhelper).
References#
- OWASP Password Storage Cheat Sheet
argon2npm packagelib/auth/password.ts(the implementation)