Time-based One-Time Passwords (TOTP) add a second authentication factor to your web applications. Even if a password is compromised, an attacker cannot log in without the TOTP code from the user's authenticator app.
How TOTP Works
# 1. Server generates a secret key and shares it with the user (QR code)
# 2. Both server and authenticator app know the secret
# 3. Every 30 seconds, a new 6-digit code is generated using:
# TOTP = HMAC-SHA1(secret, floor(time / 30))
# 4. User enters the code, server verifies it matchesServer-Side Implementation (PHP)
# Using the pragmarx/google2fa library
composer require pragmarx/google2fa
composer require bacon/bacon-qr-code
# Generate a secret for a user
use PragmaRX\Google2FA\Google2FA;
$google2fa = new Google2FA();
$secret = $google2fa->generateSecretKey();
// Store $secret in the database for this user
# Generate QR code URL
$qrUrl = $google2fa->getQRCodeUrl(
"MyApp",
$user->email,
$secret
);
# Verify a TOTP code
$valid = $google2fa->verifyKey($secret, $userInputCode);
if ($valid) {
// 2FA verification passed
} else {
// Invalid code
}Server-Side Implementation (Node.js)
// Using the otpauth library
const OTPAuth = require("otpauth");
// Generate secret
const totp = new OTPAuth.TOTP({
issuer: "MyApp",
label: user.email,
algorithm: "SHA1",
digits: 6,
period: 30,
secret: OTPAuth.Secret.generate()
});
// Get provisioning URI for QR code
const uri = totp.toString();
// Generate QR code from this URI
// Verify a code
const isValid = totp.validate({ token: userCode, window: 1 });
// window: 1 allows codes from previous and next 30-second windowUser Experience Best Practices
- Recovery codes — Generate 8-10 one-time backup codes when 2FA is enabled
- Remember device — Optionally allow skipping 2FA for 30 days on trusted devices
- Grace period — Allow the previous and next TOTP code (window=1) to account for clock skew
- Clear instructions — Show recommended authenticator apps (Authy, Google Authenticator, 1Password)
- Disable option — Allow users to disable 2FA (require current TOTP code to disable)
Security Considerations
- Store secrets encrypted in the database (not plaintext)
- Rate-limit verification attempts to prevent brute force (6-digit code = 1M possibilities)
- Use HTTPS only — TOTP setup must happen over encrypted connections
- Invalidate recovery codes after use
- Log 2FA events — Track when 2FA is enabled, disabled, or used