Skip to main content

Recovery codes

Ten single-use codes issued when you enrol an authenticator, where to keep them, how to reissue a set — and the one place they should work today and do not.

Written for whoever runs IT9 min readUpdated

A recovery code is a single-use string that stands in for your authenticator when your authenticator is not available. Ten of them are issued the moment you finish enrolling a time-based one-time-password factor, they are shown exactly once, and only hashes of them are kept.

When you get them

Two moments, and both of them are one-time:

  • When you finish enrolling TOTP from Identity → MFA, after Verify and enable accepts your first code.
  • When a sign-on policy makes you enrol during a sign-in. A policy that requires a second factor from somebody who has none does not refuse them — it walks them through adding an authenticator there and then, and hands them the same ten codes at the end. See Sign-on policies and the decision trace.

In both cases the console shows a panel headed Save your recovery codes now with an unusually direct warning, and it is accurate:

These codes are shown once and cannot be shown again — we store only hashes of them. Each one signs you in a single time if you lose your authenticator. Without them, only an administrator can restore your access.
Identity → MFA

The panel offers Copy all and Download, a checkbox reading I have saved these codes somewhere safe., and a Continue button. Use Download rather than Copy if you are on a shared machine; a clipboard outlives the tab.

What a code looks like

The shape of a recovery code
ABCDEFGH-JKMNPQRS

16 characters in two groups of eight, joined by a hyphen.
80 bits of cryptographically random data.

The alphabet is deliberately chosen to survive being read off paper and typed back in: it omits 0, O, 1, I, L and U, so the usual transcription confusions cannot occur. Two symbols — * and # — are in the alphabet, so a code containing one of those is genuine rather than corrupted.

Hyphens and spaces are stripped and the whole thing is upper-cased before it is checked, so abcdefgh jkmnpqrs and ABCDEFGH-JKMNPQRS are the same code. You do not have to reproduce the formatting exactly.

Storage is a bcrypt hash plus a separate lookup index, and the bcrypt comparison runs even after the index has found a candidate — deliberately, because skipping it would make a leak of the index directly usable. There is no path, and no support process, that reads a code back.

Where a recovery code works — and the one place it should and does not

The three places a second factor is asked for, and whether a recovery code is accepted at each.
PromptRecovery code accepted?
Step-up before a privileged action — POST /auth/step-upYes. The step-up dialog offers Use a recovery code instead and a Recovery code field. See Step-up authentication.
The second leg of a sign-in — the six-digit prompt after your passwordNo, not today. See below.
Confirming an enrolment, or disabling MFANo. Both of those ask for a live code from the authenticator by design.

What a recovery code is good for today, therefore, is the case where you still hold a session but cannot reach your authenticator: you are signed in on a laptop, you need to edit a role, the step-up prompt appears and your phone is at home. That is a real and frequent situation, and the codes solve it cleanly.

Using one, and what happens to it

Each code works exactly once. Redemption is a single atomic database operation, so two simultaneous attempts with the same code cannot both succeed — one of them is refused. Spent codes are marked rather than deleted, so an incident responder can see that a recovery code was used and when.

A wrong code, an already-used code, and a code belonging to somebody else all produce the same message, deliberately:

The one refusal
invalid or already used recovery code

Distinguishing them would tell an attacker which of their guesses was structurally correct. The cost is that you cannot tell a typo from an exhausted code, which is why the status endpoint reports how many you have left.

Reissuing a set

Go to Identity → MFA while signed in and generate a new set. You will need a current code from your authenticator to do it.

The two endpoints
GET  /api/v1/auth/mfa/recovery-codes    {"remaining": 7, "total": 10}
POST /api/v1/auth/mfa/recovery-codes    {"totp_code": "123456"}
        -> {"backup_codes": [...], "remaining": 10, "total": 10}

The live authenticator code is mandatory and the reasoning is worth repeating: a recovery code is a permanent bypass of your second factor, so minting a fresh set has to be an act of the person holding that factor, not of whoever happens to be holding the session. Somebody who has stolen a session should not be able to print themselves ten standing keys.

Two refusals you may meet: 400 invalid TOTP code if the six digits were wrong or stale, and 400 enable MFA before generating recovery codes if the account has no authenticator — the codes back up a factor, so there has to be a factor.

What removes your codes

  • Disabling your own MFA. The codes go with the factor. A recovery code that outlives the authenticator it backs up is a standing bypass on an account whose owner believes has no MFA at all.
  • An administrator clearing your MFA. The same, from the other side: DELETE /api/v1/organizations/{orgID}/users/{userID}/mfa deletes every remaining code as well as the authenticator secret.
  • Regenerating. Covered above.

What does not remove them: a password reset. Both password-reset routes change the password hash and nothing else, so somebody who resets their password keeps their factor and their codes. That is the right behaviour and it surprises people who expect a reset to be a full account recovery.

Where to actually keep them

  1. Not in the same password manager as the password, if the password manager is also where your authenticator lives. A single compromise should not hand over every factor.
  2. On paper, in the place you keep the keys to the building, for your administrators. This is the boring answer and it is the one that survives a laptop being stolen.
  3. Recorded as issued in your own notes, so that when somebody leaves you know a set exists and can clear their factor rather than assuming it lapses. Nothing expires a recovery code — a set issued today is valid until it is spent, regenerated or the factor is cleared.
  4. Regenerated after one is used. Nine remaining is not a crisis, and a set with a used code in it is a set somebody has partially seen.

One organisational point, because it is the one that catches firms out: recovery codes are not a substitute for a second administrator. They do not work at a sign-in prompt today, so an organisation whose only administrator loses their authenticator cannot be recovered by the codes that administrator holds. Keep two administrators who can clear each other's factor, and test it once. Multi-factor authentication factors has the rest of that argument.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in identity and access