HelloJohn / docs
MFA

Backup Codes

Generate and manage MFA backup codes in HelloJohn — recovery codes for when users lose access to their primary MFA device.

Backup codes are single-use recovery codes generated when a user enrolls in MFA. They're a last resort for when the user loses their MFA device.

How backup codes work

  • Generated automatically when a user enrolls their first MFA method
  • Each code is single-use — using a code invalidates it
  • 10 codes are generated by default (configurable)
  • Codes are hashed server-side — HelloJohn cannot recover them if lost

Backup codes are only shown once at enrollment time. If a user loses their codes, an admin must regenerate them — the existing codes are invalidated in the process.

Generating backup codes

Backup codes are generated automatically when a user completes their first MFA enrollment. They're returned in the enrollment response:

{
  "method_id": "mfa_01HX...",
  "backup_codes": [
    "ABCD-EFGH",
    "IJKL-MNOP",
    "QRST-UVWX",
    "YZAB-CDEF",
    "GHIJ-KLMN",
    "OPQR-STUV",
    "WXYZ-1234",
    "5678-9012",
    "3456-7890",
    "1234-5678"
  ]
}

Display these to the user and encourage them to save them in a secure location (password manager, printed copy).

Using a backup code

When a user can't access their MFA device, they use a backup code instead:

POST /v2/auth/mfa/backup-code/verify
Content-Type: application/json

{
  "challenge_id": "challenge_01HX...",
  "code": "ABCD-EFGH"
}

Response 200 OK — session issued. The used code is invalidated immediately.

SDK integration

import { useMFA } from '@hellojohn/react'

function BackupCodeInput({ challengeId }: { challengeId: string }) {
  const { submitBackupCode } = useMFA()

  return (
    <form onSubmit={e => {
      e.preventDefault()
      submitBackupCode(challengeId, (e.target as any).code.value)
    }}>
      <p>Enter one of your backup codes:</p>
      <input
        name="code"
        type="text"
        placeholder="XXXX-XXXX"
        pattern="[A-Z0-9]{4}-[A-Z0-9]{4}"
      />
      <button type="submit">Use backup code</button>
    </form>
  )
}

Regenerating backup codes

Users can regenerate backup codes from their account settings (invalidates all existing codes):

POST /v2/auth/mfa/backup-codes/regenerate
Authorization: Bearer {access_token}

Response:

{
  "backup_codes": ["NEW1-XXXX", "NEW2-XXXX", ...]
}

Admin override (for users who lost all access):

POST /v2/admin/users/{userId}/mfa/backup-codes/regenerate
Authorization: Bearer $ADMIN_TOKEN

After an admin regenerates backup codes, the user should be notified to save the new codes. The old codes are immediately invalidated.

Configuring backup code count

MFA_BACKUP_CODES_COUNT=10  # default: 10

Or per tenant:

PATCH /v2/admin/tenants/{tenantId}/auth/config
{
  "mfa_backup_codes_count": 8
}

Checking remaining backup codes

Users can see how many valid backup codes they have left:

GET /v2/auth/mfa/backup-codes/status
Authorization: Bearer {access_token}
{
  "total": 10,
  "used": 3,
  "remaining": 7
}

When remaining is low (≤ 2), prompt the user to regenerate.

On this page