ResetKit's whole job is the part of auth people get subtly wrong: sending someone a code and checking it. Here is how that works, with the reasons attached.
Issuing a code
A code is six digits, generated from a cryptographic random source with rejection sampling so no digit is favored. Six digits is a million possibilities. That sounds small, and it is, which is why nothing below relies on the code being hard to guess. It relies on the attacker not getting enough guesses.
The code is never stored. What is stored is a bcrypt hash of it, the same way a password would be. A copy of the database gives you hashes that cost real compute to reverse, per code, and every code is dead within minutes anyway.
Alongside the hash: a session id (32 random bytes, hex), an expiry, an attempt counter starting at zero, and a maximum of five.
The two clocks
A password reset session lives fifteen minutes. A two-factor challenge lives five.
The difference is deliberate. Someone resetting a password may need to find their phone, open an email client, and copy a code across devices. Someone completing a 2FA challenge is already mid-login with the code arriving on the device in their hand. Shorter is safer, and five minutes is not a burden there.
Five tries, then the code is dead
Every wrong guess increments the counter. At five the challenge flips to locked, and the only way forward is to request a new code, which means a new hash, a new expiry, and a fresh counter.
Do the arithmetic. A million possibilities, five guesses per code, and a rate limit on how often a new code can be requested. An attacker's odds per code are five in a million. The rate limit decides how many codes they can burn through, and it is set so that the answer is "not enough to matter."
That is the actual security property. Not the entropy of six digits, but the ceiling on attempts.
The line that does nothing on purpose
In the verification handler there is a line that runs when the challenge id does not exist:
await verifyCode('000000', await hashCode('dummy-timing-protection'));
It hashes a throwaway value and compares a throwaway code against it, then discards the result.
Without it, a request for a challenge that does not exist returns immediately, and a request for one that does exist takes as long as a bcrypt comparison. That difference is measurable from across the internet. An attacker could tell valid session ids from invalid ones by timing alone, without ever guessing a code.
So the invalid path pays the same cost as the valid path. The response is identical in content and roughly identical in time. Nothing is learned.
What the response says
A wrong code returns how many attempts remain. That is a usability decision that trades a little information for a lot less support email, and it is safe because the count is already bounded: knowing you have three tries left does not help you guess.
An expired or locked challenge says so plainly and tells you to request a new code, rather than pretending the code was merely wrong. Being specific about a dead end is kinder than letting someone keep trying.
Nothing here is clever
Random codes, hashed at rest, short expiries, a hard attempt cap, a rate limit on issuance, and constant-time behavior on the failure path. Each piece is standard. The reason to write it out is that the pieces only work together, and skipping any one of them quietly breaks the guarantee the others provide.