A Practical Guide to Implementing Two-Factor Authentication
Two-factor authentication (2FA) adds a critical layer of security beyond passwords. By requiring a second factor—something the user has (like a phone) or is (biometrics)—you dramatically reduce account takeover risks. This tutorial walks through a straightforward implementation using Time-based One-Time Passwords (TOTP), the most common 2FA method.
Before coding, decide which 2FA method fits your app. TOTP (e.g., Google Authenticator) is free and offline-capable. SMS codes are easier but less secure. WebAuthn (hardware keys or biometrics) offers the strongest protection. For this guide, we will focus on TOTP because it balances security and user experience.
1. Generate and Store a Secret Key
When a user enables 2FA, generate a unique random secret key per account. Use a library like otplib (Node.js) or pyotp (Python). Store the key securely in your database – encrypted at rest is best. Never expose the plain secret except during initial setup via a QR code.
// Example with otplib (Node.js)
const secret = otplib.authenticator.generateSecret();
// Save secret to user record
2. Present a QR Code for Easy Setup
Display the secret as a QR code so the user can scan it with any authenticator app. Use a library like qrcode to encode the standard otpauth:// URI. Include the issuer name (your app) and the user’s email. This makes scanning seamless.
- Format:
otpauth://totp/{issuer}:{email}?secret={secret}&issuer={issuer} - Test scanning with Google Authenticator, Authy, or Microsoft Authenticator.
3. Verify the First Code
Before enabling 2FA, ask the user to enter the code from their authenticator app. Validate it using the same library with the stored secret. This proves they have scanned the QR correctly and prevents lockouts.
const isValid = otplib.authenticator.check(token, secret);
if (isValid) { /* enable 2FA */ }
4. Handle Login and Backup Codes
During login, prompt for the TOTP code after password verification. Also generate a set of single-use backup codes (e.g., 8 codes of 8 digits). Store them as hashed values. Give these to the user to safekeep for account recovery.
- Rate-limit TOTP attempts to prevent brute force.
- Provide a “remember this device” option (trust token) for convenience.
Conclusion
Implementing TOTP-based 2FA requires only a few steps: secret generation, QR display, code verification, and backup codes. Use well-tested libraries, secure your secrets, and always test the full flow. Your users will thank you for the extra security.