Recovery codes are single-use backup credentials that let someone log in when their normal second factor — an authenticator app, a security key, an SMS code — is unavailable. When a user enables two-factor authentication, the service typically hands them a set of ten codes to print or save in a password manager. If they lose their phone, one of those codes gets them back in. This tool generates a batch of cryptographically random codes in the familiar xxxx-xxxx style used by GitHub, Google, and similar services, entirely in your browser.
How it works
Each code is built from secure random bytes, then mapped onto a clear, unambiguous character set:
- Draw random values from
crypto.getRandomValues— the browser’s cryptographically secure RNG. - Map each value onto an alphabet that omits visually confusing characters (no
0/Oor1/l/I) to reduce transcription errors. - Insert a hyphen at the chosen group size to make the code readable, producing forms like
a8k3f-2m9qz.
Because each code is generated independently from secure randomness, knowing one code tells an attacker nothing about the others.
How many codes and how long?
Most major services issue 8 to 16 recovery codes. The trade-off is practical:
- Too few (3–4) risks a user running out if they authenticate during device-loss emergencies several times before regenerating.
- Too many (30+) makes the list unwieldy to manage and print, and more codes are a larger credential surface if the list is stolen.
Code length similarly balances security and usability. A 10-character alphanumeric code (excluding ambiguous characters) from a 30-character alphabet provides more than 570 trillion possible values per code — far exceeding any practical brute-force attack, especially when combined with rate limiting and lockout on the server. Longer codes add security depth with little user cost when codes are stored in a password manager rather than memorized.
Server-side implementation notes
Recovery codes require careful server-side handling:
- Hash before storing. Treat each code like a password: hash it with bcrypt, scrypt, or Argon2 with a per-code salt. Never store recovery codes in plain text, even in a separate table.
- Mark consumed immediately. On successful use, mark the code as consumed inside the same database transaction as the login. Failure to do this allows a race condition where the same code is used twice.
- Rate limit redemption attempts. Apply the same brute-force protection as you would a password login: lock the account or impose delays after several consecutive failed attempts.
- Prompt to regenerate. When a user redeems a code (or falls below two or three remaining), prompt them to generate and save a fresh set. A user who burns most of their codes may not remember to refresh them.
Storage and lifecycle for end users
- Keep codes somewhere reachable without the primary device: a password manager, a printed sheet stored securely, or an encrypted note in a separate application.
- Never store them in the same place as the password for the account they protect — if an attacker has the password storage, they should not also have the recovery codes.
- Treat the set as single-use: each code works exactly once. When most are spent, regenerate a fresh set and treat the old set as expired.
Everything here runs locally — generated codes never touch a network. Copy them straight into your enrollment flow or test fixtures.