Two Factor Authentication (2FA): Complete Setup Guide
Passwords alone are not enough. Credential stuffing, phishing, and data breaches expose billions of passwords every year. Two factor authentication (2FA) ensures that even if your password is stolen, an attacker still cannot get in. This guide covers every 2FA method, how TOTP works under the hood, and how to implement it in your own application.
The Problem: Passwords Are Not Enough
In 2024 alone, over 1.5 billion credentials were exposed in data breaches. Most users reuse passwords across sites, which means a breach at one service can cascade to dozens of others through credential stuffing attacks. A strong, unique password helps - but it is still a single point of failure.
Two factor authentication adds a second verification step that is separate from your password. Even if an attacker obtains your password from a breach database or through a phishing attack, they cannot log in without the second factor - something you physically possess (your phone, a hardware key) or something derived from a secret only you know.
According to Google's own research, adding any form of 2FA to an account blocks 100% of automated bot attacks, 96% of bulk phishing attacks, and 76% of targeted attacks. It is the single highest-impact security action most users can take.
The Three Types of 2FA (Ranked by Security)
Strongest: FIDO2 / Passkeys / Hardware Security Keys
Cryptographic challenge-response using a hardware device (YubiKey) or platform authenticator (Face ID, Windows Hello). Phishing-resistant by design. Cannot be intercepted or relayed.
Strong: TOTP (Authenticator Apps)
Time based one time passwords generated by apps like Google Authenticator, Authy, or 1Password. Resistant to replay attacks. Vulnerable to real time phishing but significantly harder to attack than SMS.
Moderate: Push Notifications (Duo, Microsoft Authenticator)
A push notification is sent to your registered device for approval. Convenient but vulnerable to MFA fatigue attacks where attackers spam notifications until the user accidentally approves.
Weak: SMS / Voice Call OTP
A one time code sent by text message. Vulnerable to SIM swapping, SS7 protocol attacks, and interception. Better than nothing, but should be avoided for high value accounts.
How TOTP Works: The Math Behind Google Authenticator
TOTP (Time-Based One-Time Password, defined in RFC 6238) is the standard behind Google Authenticator, Authy, and most authenticator apps. Understanding how it works helps you implement it correctly and debug issues with clock drift.
The Setup Phase
When you enable 2FA on a service, the server generates a random shared secret - typically 20 bytes of random data encoded as Base32 (e.g., JBSWY3DPEHPK3PXP). This secret is shared once (via QR code scan) and never transmitted again. Both the server and your authenticator app store this secret permanently.
The Code Generation Algorithm
Every 30 seconds, the authenticator app computes a 6-digit code using this process:
- Get the current time step: Divide the current Unix timestamp by 30 to get an integer counter
T. - Compute HMAC-SHA1: Calculate
HMAC-SHA1(secret, T)whereTis encoded as an 8-byte big-endian integer. - Dynamic truncation: Take the last nibble of the HMAC output as an offset. Extract 4 bytes starting at that offset.
- Truncate to 6 digits: Take the 31 least significant bits of those 4 bytes and compute
result mod 10^6.
The server performs the exact same computation. If the codes match, authentication succeeds. Most implementations also accept codes from the previous and next 30-second window to handle clock drift.
// TOTP verification in Node.js (conceptual)
const crypto = require('crypto');
function generateTOTP(secret, timeStep = 30) {
// Decode Base32 secret to bytes
const key = base32Decode(secret);
// Get current time counter
const counter = Math.floor(Date.now() / 1000 / timeStep);
// Encode counter as 8-byte big-endian buffer
const counterBuffer = Buffer.alloc(8);
counterBuffer.writeBigInt64BE(BigInt(counter));
// HMAC-SHA1
const hmac = crypto.createHmac('sha1', key).update(counterBuffer).digest();
// Dynamic truncation
const offset = hmac[hmac.length - 1] & 0x0f;
const code = (
((hmac[offset] & 0x7f) << 24) |
((hmac[offset + 1] & 0xff) << 16) |
((hmac[offset + 2] & 0xff) << 8) |
(hmac[offset + 3] & 0xff)
) % 1000000;
return String(code).padStart(6, '0');
}
Step-by-Step: Setting Up TOTP 2FA on Popular Services
GitHub
- Go to Settings → Password and authentication → Two factor authentication
- Click Enable two factor authentication
- Select Authenticator app
- Scan the QR code with Google Authenticator, Authy, or 1Password
- Enter the 6-digit code to confirm setup
- Download your recovery codes and store them in a secure location (password manager)
Google Account
- Go to myaccount.google.com → Security → 2-Step Verification
- Click Get started and re-enter your password
- Select Authenticator app (choose TOTP over SMS if possible)
- Scan the QR code with your authenticator app
- Enter the verification code and click Turn on
- Optionally add a backup phone number as a fallback
AWS Console
- Log in to the AWS Console and click your username → Security credentials
- Under Multi-factor authentication (MFA), click Assign MFA device
- Select Authenticator app and click Next
- Click Show QR code and scan with your authenticator
- Enter two consecutive codes (the first and then the next one 30 seconds later) to verify
- Click Add MFA
Test TOTP Generation Online
Use our free TOTP Generator to generate and verify time based one time passwords. Enter any Base32 secret and see live 6-digit codes update every 30 seconds. Useful for testing your 2FA implementation.
Open TOTP GeneratorImplementing 2FA in Your Own Application
Backend: Generating and Verifying TOTP Secrets
// Node.js with the 'otplib' package (npm install otplib)
const { authenticator } = require('otplib');
// 1. Generate a secret for a new user (store this in your database)
const secret = authenticator.generateSecret(); // e.g., "JBSWY3DPEHPK3PXP"
// 2. Generate the otpauth URI for QR code display
const otpauthUrl = authenticator.keyuri(
'alice@example.com', // user identifier
'MyApp', // issuer name
secret
);
// Use a QR code library to render otpauthUrl as a QR code
// 3. Verify a code submitted by the user
function verify2FA(userProvidedCode, storedSecret) {
return authenticator.verify({
token: userProvidedCode,
secret: storedSecret
});
}
Frontend: QR Code Display
// Display QR code using qrcode library
const QRCode = require('qrcode');
app.get('/setup-2fa', requireAuth, async (req, res) => {
const secret = authenticator.generateSecret();
// Store secret temporarily (not confirmed yet)
req.session.pending2faSecret = secret;
const otpauthUrl = authenticator.keyuri(
req.user.email,
'MyApp',
secret
);
const qrCodeDataUrl = await QRCode.toDataURL(otpauthUrl);
res.render('setup-2fa', { qrCode: qrCodeDataUrl });
});
// Confirm 2FA setup
app.post('/confirm-2fa', requireAuth, (req, res) => {
const { code } = req.body;
const secret = req.session.pending2faSecret;
if (authenticator.verify({ token: code, secret })) {
// Save secret to database permanently
db.users.update(req.user.id, { twoFactorSecret: secret, twoFactorEnabled: true });
delete req.session.pending2faSecret;
res.json({ success: true });
} else {
res.status(400).json({ error: 'Invalid code' });
}
});
Recovery Codes
Always implement recovery codes. If a user loses access to their authenticator app, recovery codes are the only way back in. Generate 8–10 single-use codes at setup time, display them once, and store salted hashes in your database (never plain text):
const crypto = require('crypto');
const bcrypt = require('bcrypt');
function generateRecoveryCodes(count = 10) {
return Array.from({ length: count }, () =>
crypto.randomBytes(5).toString('hex').toUpperCase() // e.g., "A3F2C1B4D9"
);
}
// Store hashed versions
const codes = generateRecoveryCodes();
const hashed = await Promise.all(codes.map(c => bcrypt.hash(c, 10)));
db.users.update(userId, { recoveryCodes: hashed });
// Return plain codes to user once - never again
Why SMS 2FA Is Weak (and When It Still Makes Sense)
SMS 2FA has well-documented vulnerabilities:
- SIM swapping: An attacker contacts your carrier, impersonates you, and transfers your phone number to a SIM they control. All your SMS codes then go to them. This attack has been used to hijack accounts worth millions of dollars in cryptocurrency.
- SS7 attacks: The SS7 protocol that routes phone calls and SMS was designed in the 1970s with no authentication. Nation-state actors and sophisticated criminals can intercept SMS messages by exploiting SS7 vulnerabilities.
- Phishing: Real time phishing pages can relay SMS codes instantly. The attacker's fake login page forwards your credentials and code to the real site before the 30-second window expires.
That said, SMS 2FA is dramatically better than no 2FA at all. For consumer applications where TOTP adoption is low, SMS provides a meaningful security improvement over password-only authentication. Just do not rely on it for high value accounts like email, banking, or cryptocurrency.
NIST SP 800-63B deprecated SMS OTP as a two factor authentication method in 2017, citing SIM swapping and SS7 vulnerabilities. For any application handling sensitive data, TOTP or FIDO2 should be the standard.
FAQ
What is the best authenticator app in 2026?
Ente Auth is the top recommendation for most users - it is open source, offers encrypted cloud backup, and works on all platforms. 1Password and Bitwarden integrate TOTP directly into their password managers for convenience. Authy has encrypted sync but is a closed-source proprietary app. Google Authenticator now supports Google Account sync but lacks open source auditability. Avoid apps that store TOTP secrets without encryption.
What happens if I lose my phone with Google Authenticator?
This is why recovery codes are critical. When you set up 2FA, you should always: (1) save the recovery codes in a password manager or printed and stored securely; (2) scan the setup QR code into a second authenticator app on a backup device; (3) for important accounts, store the Base32 secret itself in your password manager so you can re-register the authenticator from scratch.
Can TOTP codes be phished?
Yes. Unlike FIDO2/passkeys, TOTP is not phishing resistant. A sophisticated real time phishing attack can capture your TOTP code and relay it to the real site within the 30-second validity window. This is why passkeys and hardware security keys are superior for high value accounts. For most users and applications, TOTP provides excellent protection against the vast majority of attacks.
What is the difference between TOTP and HOTP?
HOTP (HMAC-based One-Time Password, RFC 4226) uses an incrementing counter rather than the current time. Each code is only valid once and never expires. TOTP (RFC 6238) extends HOTP by using the Unix timestamp divided by 30 as the counter, giving time-limited 30-second windows. TOTP is preferred because HOTP codes can be captured and replayed if the counter is not synchronized, while TOTP codes expire after 30 seconds.
How do I add 2FA to a Django or Rails app?
For Django, use django-otp or django-two factor-auth. For Rails, use devise-two factor with the rotp gem. For Node.js/Express, use otplib. All implement RFC 6238 TOTP and provide enrollment flows, QR code generation, and verification middleware. The key steps are: generate and store a per-user secret, display a QR code for app setup, verify a code during enrollment, and then require code verification on each login after password authentication passes.
Use our free tool here → TOTP / 2FA Generator - generate live TOTP codes from any Base32 secret, validate codes, and test your 2FA implementation without needing a phone.
Usman has 10+ years of experience securing enterprise infrastructure, managing high-traffic servers, and building zero-knowledge security tools. Read more about the author.