MFA Backup and Recovery

Protect, regenerate, and use AuthOS MFA backup codes and recover locked-out accounts

AuthOS release 0.8.2 TypeScript SDK 0.8.0 Latest-only documentation
Updated Jul 15, 2026 Edit this page
On this page

Each user receives 10 single-use backup codes during MFA setup. Recovery is part of the security boundary: show codes only when generated, keep them out of logs, and define a verified support path before users can lose access.

Managing Backup Codes

Regenerating Backup Codes

Regeneration invalidates every previous backup code. Warn the user and require an explicit confirmation before continuing.

async function regenerateBackupCodes() {
  try {
    const result = await sso.user.mfa.regenerateBackupCodes();

    console.log('New backup codes:', result.backup_codes);

    // IMPORTANT: Display new codes to the user once.
    // Previous codes are now invalid.
    return result.backup_codes;
  } catch (error) {
    console.error('Failed to regenerate backup codes:', error);
    throw error;
  }
}

The console.log calls above are illustrative. Remove them in production so recovery credentials never enter browser or observability logs.

Backup Codes Management Component

import { useState } from 'react';
import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';

interface BackupCodesManagerProps {
  sso: SsoClient;
}

export function BackupCodesManager({ sso }: BackupCodesManagerProps) {
  const [codes, setCodes] = useState<string[]>([]);
  const [showCodes, setShowCodes] = useState(false);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const regenerateCodes = async () => {
    const confirmed = window.confirm(
      'This will invalidate your current backup codes. Continue?'
    );

    if (!confirmed) return;

    setLoading(true);
    setError('');

    try {
      const result = await sso.user.mfa.regenerateBackupCodes();
      setCodes(result.backup_codes);
      setShowCodes(true);
    } catch (err) {
      if (err instanceof SsoApiError) {
        setError(err.message);
      } else {
        setError('Failed to regenerate backup codes');
      }
    } finally {
      setLoading(false);
    }
  };

  const downloadCodes = () => {
    const blob = new Blob([codes.join('\n')], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `backup-codes-${Date.now()}.txt`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const copyCodes = () => navigator.clipboard.writeText(codes.join('\n'));

  return (
    <div className="backup-codes-manager">
      <h3>Backup Codes</h3>
      <p>
        Backup codes allow you to access your account if you lose your
        authenticator device. Each code can only be used once.
      </p>
      {showCodes && codes.length > 0 && (
        <div className="codes-display">
          <div className="warning">
            <strong>Save these codes now!</strong> They won't be shown again.
          </div>
          <div className="codes-grid">
            {codes.map((code, index) => <code key={index}>{code}</code>)}
          </div>
          <div className="actions">
            <button onClick={copyCodes}>Copy All</button>
            <button onClick={downloadCodes}>Download</button>
            <button onClick={() => setShowCodes(false)}>Hide</button>
          </div>
        </div>
      )}
      {!showCodes && (
        <div className="regenerate-section">
          <p>
            Need new backup codes? Regenerating will invalidate all previous codes.
          </p>
          <button onClick={regenerateCodes} disabled={loading}>
            {loading ? 'Generating...' : 'Regenerate Backup Codes'}
          </button>
        </div>
      )}
      {error && <div className="error">{error}</div>}
    </div>
  );
}

Recovery Flows

Lost Authenticator Device

If a user loses the authenticator device, prefer a backup code:

// During login, the user can enter a backup code instead of TOTP.
const tokens = await sso.auth.verifyMfa(preauthToken, backupCode);

If the user also lost all backup codes:

  1. Send the user through your verified support or administrator process.
  2. Have an authorized administrator disable MFA for that user.
  3. Let the user sign in with the remaining allowed method.
  4. Require fresh MFA enrollment on the replacement device.

The support process must authenticate the user independently. Do not disable a factor based only on possession of the email address or a request from an unverified support channel.

Lost Backup Codes

If the user still has the authenticator device, regenerate codes from an authenticated session:

const newCodes = await sso.user.mfa.regenerateBackupCodes();

Backup Code Usage

// Backup codes are single-use and cannot be reused after verification.
const tokens = await sso.auth.verifyMfa(preauthToken, 'ABCD-EFGH-IJKL');

Test the lost-device and lost-code paths before launch. Continue to Administration for deliberate disablement and support escalation.