Skip to main content
Recovery codes are single-use backup codes that allow users to access their account if they lose access to their primary MFA method (TOTP authenticator, security key, etc.).

How Recovery Codes Work

Recovery codes are:
  • Automatically generated when a user enables their first MFA method
  • Single-use - each code can only be used once
  • Numeric - typically 8-digit codes by default
  • Cryptographically secure - generated using HMAC-SHA1 with a random seed

Configuration

Recovery codes are enabled by default:
settings.py

Settings Reference

MFA_RECOVERY_CODE_COUNT

Default: 10 The number of recovery codes to generate for each user.

MFA_RECOVERY_CODE_DIGITS

Default: 8 The number of digits in each recovery code.
Longer codes are more secure but harder to type. 8 digits provides a good balance between security and usability.

URL Endpoints

Recovery code URLs are available at:
  • /accounts/mfa/recovery-codes/ - View unused recovery codes
  • /accounts/mfa/recovery-codes/generate/ - Generate new recovery codes
  • /accounts/mfa/recovery-codes/download/ - Download codes as text file

Automatic Generation

Recovery codes are automatically generated when a user activates their first MFA method:

Viewing Recovery Codes

Users can view their unused recovery codes at any time (requires reauthentication):
templates/mfa/recovery_codes/index.html

Downloading Recovery Codes

Users can download their codes as a plain text file:
The response includes the header:

Regenerating Recovery Codes

Users can generate new recovery codes (invalidates old ones):
templates/mfa/recovery_codes/generate.html

Programmatic Usage

Generate Recovery Codes

Validate a Recovery Code

Check Remaining Codes

Regenerate Codes

How Codes Are Generated

Recovery codes are generated using HMAC-SHA1:

Storage Format

Recovery codes are stored in the Authenticator model:
The used_mask is a bitmask where each bit represents whether a code has been used:
  • Bit 0 = first code
  • Bit 1 = second code
  • etc.

Encryption

The seed is encrypted before storage:
To add custom encryption, override the adapter:
myapp/adapter.py
settings.py

Migration Support

If you’re migrating from another system with existing recovery codes:
Migrated codes:
  • Are stored as an encrypted list
  • Are removed from the list when used
  • Don’t use the seed-based generation

Forms Customization

Override the recovery code generation form:
settings.py
myapp/forms.py

User Experience Best Practices

Show Codes Immediately After Generation

After enabling MFA, redirect users to view their recovery codes:

Warn When Codes Are Low

Download Instructions

Encourage users to download and securely store their codes:

Security Considerations

Single Use

Each code can only be used once. After validation, the code is marked as used:

Secure Storage

Users should store recovery codes:
  • In a password manager
  • In a secure physical location (safe, lockbox)
  • Never in plain text on their device

Regeneration

When codes are regenerated:
  • All old codes are invalidated
  • A new seed is generated
  • Users must download/save the new codes

Testing

Generate Test Codes

Test Code Validation

Common Issues

Codes Not Generated

Ensure recovery codes are enabled:

Can’t View Codes

Check that the user has MFA enabled:

Codes Already Used

If all codes are used, users must:
  1. Use an alternative MFA method (TOTP, WebAuthn) to log in
  2. Generate new recovery codes
If locked out, administrators can:
  • Disable MFA for the user in Django admin
  • Or generate new codes programmatically