Password Reset and MFALesson 6.5
How to use backup codes for MFA account recovery
backup code generation, hashing backup codes, one-time use enforcement, backup code redemption endpoint, code regeneration, user education
Generating and Storing Backup Codes
Backup codes are single-use fallback codes provided when TOTP is enabled. If a user loses their authenticator device, these codes let them log in and disable MFA.
function generateBackupCodes() {
return Array.from({ length: 10 }, () =>
crypto.randomBytes(5).toString('hex') // 10-char hex code
);
}
// Store hashed codes in DB
async function storeBackupCodes(userId, codes) {
const hashed = await Promise.all(
codes.map(code => bcrypt.hash(code, 10))
);
await db.insertBackupCodes(userId, hashed);
}
// Redemption endpoint
app.post('/auth/backup-code', async (req, res) => {
const userId = req.session.pendingUserId;
const { code } = req.body;
const storedCodes = await db.getUnusedBackupCodes(userId);
let matched = null;
for (const stored of storedCodes) {
if (await bcrypt.compare(code, stored.hash)) {
matched = stored;
break;
}
}
if (!matched) return res.status(401).json({ error: 'Invalid code' });
await db.markBackupCodeUsed(matched.id);
delete req.session.pendingUserId;
req.session.regenerate((err) => {
req.session.userId = userId;
res.json({ message: 'Logged in via backup code', codesRemaining: storedCodes.length - 1 });
});
});
Show backup codes to the user exactly once, immediately after TOTP setup. Never display them again — treat them exactly like a password. Warn users when they have fewer than 3 codes remaining and offer code regeneration (which invalidates all existing codes).
