When you build on DontCode, your users' credentials are protected by the same security standards we hold ourselves to. Here is exactly what we do, and what you do not have to think about.
If you ship an app on DontCode and someone asks how you handle their password, you should be able to point at this page and move on. Authentication is one of those areas where the wrong default quietly turns into a future incident. We pick the right defaults so you do not have to. This page covers how passwords are stored, how brute-force attempts are stopped, how second factors work, and how compromised sessions get cut off.
When we think about security for the apps our clients ship, we have to be 100% defensive. So we built a small rubric and we run our own auth system through it on a regular cycle. This page shows the current results, criterion by criterion. We are also exploring an external audit when we find the right team for it.
Each category is graded against 4 fixed binary criteria. Every criterion is either PASS or FAIL, no partial credit. The same criteria apply to every category, so there is no subjective weighting.
C1
Documented
C2
Correct
C3
Edge cases
C4
Auditable
Six of seven categories pass every criterion. Two open items remain. Both are listed below and queued for the next cycle.
IP-level / cross-account rate limiting
Brute-force & rate limiting 路 C4 路 Auditable
Add an IP-level rate-limit layer on top of the existing per-email limits, so distributed credential stuffing spreading across many source IPs and many accounts cannot fly under the radar.
TOTP at-rest encryption details
MFA & session management 路 C4 路 Auditable
Pin down and publish the exact algorithm, key length, and key-storage layout used by pgcrypto for TOTP secrets, so the strength of the at-rest encryption is independently checkable.
This is an internal review using our own rubric. It is not a third-party certification. We are exploring an external review when we find a team that can do this well.
We never store your users' passwords. Every password is run through Argon2id, the winner of the international Password Hashing Competition and the algorithm currently recommended by OWASP as the gold standard. Only the resulting fingerprint is persisted.
Argon2id is memory-hard by design, which means it resists the GPU and ASIC-based cracking attacks that have made older schemes like MD5, SHA-1, and even bcrypt increasingly risky at scale. We pin the work-factor parameters explicitly, so the cost of cracking a stolen hash stays exactly where we set it. No silent drift from library updates.
Current parameters
Every password gets its own cryptographically random salt before hashing. Two of your users sharing the same password still produce completely different stored hashes. Rainbow tables and precomputed attacks simply do not apply.
Sign-in checks use Argon2's constant-time comparison, closing off the timing side-channels that could otherwise leak information about stored hashes one byte at a time.
Every DontCode app has its own isolated auth pool. Credentials from one client's user base are never reachable from another's. Even at the database level, rows are scoped by pool ID, and our access patterns enforce that boundary on every query.
Every sign-in attempt is counted against two limits, scoped per email address. A short-burst throttle stops bots from hammering an account in real time. A longer lockout window stops slow, distributed brute-force attempts that try to dodge a single rate limit. Counters reset on a successful sign-in and on password reset.
A locked account returns a distinct "Account locked" response, separate from a normal sign-in failure. That tells your legitimate user something is wrong and that a password reset will clear it, without leaking anything to an attacker who is just guessing emails.
Current limits
Your users can pair an authenticator app (Authy, 1Password, Google Authenticator, and any other RFC 6238 TOTP client) as a second factor. On enrollment, the service generates 8 single-use recovery codes for them to store safely. Recovery codes are hashed at rest and burned after a single use.
Sign-in with MFA enabled does not return a session directly. After the password check, the service issues a short-lived (5 minute) challenge token. The user then submits their TOTP code, or a recovery code, to the challenge endpoint to complete sign-in. TOTP secrets themselves are encrypted at rest with pgcrypto. OAuth sign-in skips this step on purpose, because the identity provider already enforces its own MFA.
Every issued access token embeds a session epoch, a monotonic counter unique to that user. On every authenticated request, the service verifies the epoch is still current. Tokens are not just trusted because they decode.
When a user resets their password, the service bumps the epoch atomically. Every previously issued token for that user becomes invalid immediately, with no waiting for natural expiry. This closes a class of attacks where stolen credentials let an attacker keep working even after the legitimate user changes their password. If the bump fails, the password reset itself fails. We fail closed.
A good hashing algorithm is necessary but not sufficient. Here is the rest of what we do around it.
Passwords exist in memory only long enough to hash or verify. They are never written to logs, never included in error messages, and never returned in API responses.
Failed sign-in attempts return the same error whether the email is unknown or the password is wrong. The service never reveals whether a given email is registered in your pool.
Password reset tokens expire quickly, can only be used once, and are bound to verified email delivery. A leaked or replayed token is useless.
All authentication traffic is encrypted in transit with TLS. There is no fallback path, and no "internal" endpoints that bypass encryption.
Each project can tighten the password policy independently: minimum length, required character classes, and so on. Defaults are sensible; stricter is one toggle away.
Our auth service is intentionally small. Password hashing, rate limiting, MFA, and session revocation each live in their own focused file under lib/auth/ (credentials.ts, rate-limit.ts, mfa.ts, session-epoch.ts), so every piece is easy to reason about, easy to review internally, and easy to point your security team at if they ask. We would rather be boring and correct here than clever.
You do not manage hashing parameters, salt generation, key rotation, rate limit infrastructure, MFA libraries, or session handling. We do, and we keep it current with industry guidance. You ship your product. We keep your users' credentials safe.
Security teams asking about specifics, compliance auditors needing detail, or anyone curious about implementation choices: reach out and we will walk you through it.