Multi-Factor Authentication Implementation: Adding Extra Security Layers

Multi-Factor Authentication Implementation: Adding Extra Security Layers

Whitespots Team ·
mfa
2fa
authentication
totp

Introduction

Multi-Factor Authentication (MFA) significantly reduces account compromise risk by requiring multiple verification methods. This guide covers implementing TOTP-based MFA, SMS codes, and backup codes.

TOTP Implementation

javascript
const speakeasy = require('speakeasy'); const QRCode = require('qrcode'); // Enable MFA for user app.post('/mfa/enable', async (req, res) => { const secret = speakeasy.generateSecret({ name: `MyApp (${req.user.email})`, issuer: 'MyApp' }); // Store secret temporarily await redis.setex( `mfa:setup:${req.user.id}`, 300, secret.base32 ); // Generate QR code const qrCode = await QRCode.toDataURL(secret.otpauth_url); res.json({ secret: secret.base32, qrCode }); }); // Verify and activate MFA app.post('/mfa/verify', async (req, res) => { const { token } = req.body; const secret = await redis.get(`mfa:setup:${req.user.id}`); const verified = speakeasy.totp.verify({ secret, encoding: 'base32', token, window: 2 }); if (verified) { // Save secret to user account await db.users.update(req.user.id, { mfaSecret: encrypt(secret), mfaEnabled: true }); // Generate backup codes const backupCodes = generateBackupCodes(); await saveBackupCodes(req.user.id, backupCodes); res.json({ success: true, backupCodes }); } else { res.status(400).json({ error: 'Invalid code' }); } });

Login with MFA

javascript
app.post('/login', async (req, res) => { const { username, password } = req.body; const user = await authenticateUser(username, password); if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } if (user.mfaEnabled) { // Create temporary session const tempToken = generateTempToken(user.id); return res.json({ requiresMfa: true, tempToken }); } // No MFA required const session = await createSession(user.id); res.json({ token: session.token }); }); app.post('/mfa/verify-login', async (req, res) => { const { tempToken, code } = req.body; const userId = verifyTempToken(tempToken); const user = await db.users.findById(userId); const verified = speakeasy.totp.verify({ secret: decrypt(user.mfaSecret), encoding: 'base32', token: code, window: 2 }); if (verified) { const session = await createSession(user.id); res.json({ token: session.token }); } else { res.status(401).json({ error: 'Invalid MFA code' }); } });

Backup Codes

javascript
const crypto = require('crypto'); function generateBackupCodes(count = 10) { return Array.from({ length: count }, () => crypto.randomBytes(4).toString('hex').toUpperCase() ); } async function saveBackupCodes(userId, codes) { const hashed = codes.map(code => crypto.createHash('sha256').update(code).digest('hex') ); await db.backupCodes.insertMany( hashed.map(hash => ({ userId, codeHash: hash, used: false })) ); } // Use backup code app.post('/mfa/backup-code', async (req, res) => { const { tempToken, backupCode } = req.body; const userId = verifyTempToken(tempToken); const hash = crypto.createHash('sha256').update(backupCode).digest('hex'); const code = await db.backupCodes.findOne({ userId, codeHash: hash, used: false }); if (code) { await db.backupCodes.update(code.id, { used: true }); const session = await createSession(userId); res.json({ token: session.token }); } else { res.status(401).json({ error: 'Invalid backup code' }); } });

MFA Best Practices

  • ✅ Support multiple MFA methods
  • ✅ Provide backup codes
  • ✅ Allow MFA recovery process
  • ✅ Rate limit MFA attempts
  • ✅ Use time-based tokens (TOTP)
  • ✅ Implement proper token window
  • ✅ Encrypt stored MFA secrets
  • ✅ Log MFA events
  • ✅ Support remember device option
  • ✅ Allow users to disable MFA securely

Conclusion

MFA dramatically improves account security by requiring multiple authentication factors. Implement TOTP-based authentication with backup codes and recovery mechanisms for a robust MFA system.