AiTechWorlds
AiTechWorlds
Multi-Factor Authentication (MFA) requires users to verify identity using two or more factors from different categories:
2FA (Two-Factor Authentication) is MFA with exactly two factors. Any MFA implementation significantly reduces account takeover risk, even if the password is compromised.
| Method | Security | Phishing Resistant | UX | Cost |
|---|---|---|---|---|
| FIDO2 / Passkey | Highest | Yes | Excellent | Hardware optional |
| Hardware key (YubiKey) | Highest | Yes | Good | $25β80/key |
| TOTP app (Authenticator) | High | No | Good | Free |
| Push notification | High | Partial | Excellent | Varies |
| SMS OTP | Medium | No | Good | Per SMS |
| Email OTP | Low-Medium | No | Good | Free |
| Security question | Low | No | Poor | Free |
TOTP is the most widely deployed MFA method, used by Google Authenticator, Authy, Microsoft Authenticator.
1. Setup: Server generates a random secret (20 bytes) and shows it as QR code
2. User scans QR with authenticator app
3. App and server share the secret
At login:
4. Server generates code: HMAC-SHA1(secret, floor(now/30))
5. User opens app β app generates same code independently
6. User enters code β server verifies match
7. Code expires after 30 secondsimport pyotp
import qrcode
# Generate secret (store securely per user)
secret = pyotp.random_base32()
# JBSWY3DPEHPK3PXP (store this for the user)
# Generate TOTP URI for QR code
totp = pyotp.TOTP(secret)
uri = totp.provisioning_uri(
name="alice@example.com",
issuer_name="MyApp"
)
# otpauth://totp/MyApp:alice@example.com?secret=...&issuer=MyApp
# Generate QR code
qr = qrcode.make(uri)
qr.save("qr.png")
# Verify user input
def verify_totp(user_secret: str, user_input: str) -> bool:
totp = pyotp.TOTP(user_secret)
return totp.verify(user_input, valid_window=1) # Β±1 window for clock driftβ Store the secret encrypted at rest
β One-time use per code β log and reject previously used codes (replay attack prevention)
β valid_window=1 (accept Β±30s) accommodates clock drift
β Never accept multiple windows (valid_window β₯ 3) β weakens securityCounter-based OTP β code advances with each use rather than by time.
import pyotp
hotp = pyotp.HOTP(secret)
print(hotp.at(0)) # Code at counter 0
print(hotp.at(1)) # Code at counter 1
# Verify (with counter sync window)
hotp.verify("123456", counter=5, look_ahead_window=3)While better than no MFA, SMS OTP is vulnerable to:
import random
import string
from datetime import datetime, timedelta
def generate_otp(length=6) -> str:
return ''.join(random.choices(string.digits, k=length))
def send_otp(phone: str, otp: str):
# Store OTP hash + expiry in database
expiry = datetime.utcnow() + timedelta(minutes=10)
db.store_otp(phone, hash_otp(otp), expiry)
sms_provider.send(phone, f"Your verification code: {otp}. Valid for 10 minutes.")
def verify_otp(phone: str, user_input: str) -> bool:
stored = db.get_otp(phone)
if not stored or stored.expiry < datetime.utcnow():
return False
if stored.attempts >= 3: # rate limit
raise TooManyAttempts()
db.increment_attempts(phone)
return hmac.compare_digest(hash_otp(user_input), stored.hash)The strongest and most phishing-resistant authentication method. The private key never leaves the device.
Registration:
1. Server sends challenge (random bytes)
2. Browser calls navigator.credentials.create()
3. Authenticator creates key pair, signs the challenge
4. Public key + credential ID sent to server β stored
5. Private key stays on device (never transmitted)
Authentication:
1. Server sends challenge
2. Browser calls navigator.credentials.get()
3. Authenticator signs challenge with private key
4. Server verifies signature with stored public key
5. Login granted β no password ever transmitted// WebAuthn Registration
const credential = await navigator.credentials.create({
publicKey: {
challenge: serverChallenge, // random bytes from server
rp: { name: "MyApp", id: "myapp.com" },
user: { id: userId, name: email, displayName: name },
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 }, // RS256
],
authenticatorSelection: {
userVerification: "required", // biometric or PIN
},
timeout: 60000,
}
});
// Send credential.response to server for verification and storage
// WebAuthn Authentication
const assertion = await navigator.credentials.get({
publicKey: {
challenge: serverChallenge,
rpId: "myapp.com",
allowCredentials: [{ id: credentialId, type: "public-key" }],
userVerification: "required",
}
});
// Send assertion to server for verificationβ Offer multiple MFA methods (TOTP + SMS as backup)
β Provide recovery codes at enrollment (8β12 single-use codes)
β Store recovery codes as hashed values, not plaintext
β Send enrollment confirmation email
β Allow trusted devices (skip MFA for 30 days on same device)
β Show when MFA was last used
β Force re-enrollment if secret compromised
β Rate limit MFA attempts (lock after 5 failures)import secrets
def generate_recovery_codes(count=10, length=10) -> list[str]:
codes = []
for _ in range(count):
# Format: XXXXX-XXXXX
raw = secrets.token_hex(length // 2)
formatted = f"{raw[:5]}-{raw[5:]}"
codes.append(formatted)
return codes
# Show to user ONCE, store hashed
hashed_codes = [bcrypt.hashpw(c.encode(), bcrypt.gensalt()) for c in codes]Advertisement
Get more notes like this daily on Telegram!
Free study notes, cheat sheets & AI tips
Last reviewed on June 13, 2026 by the AiTechWorlds Notes Team. Free cheat sheet β no signup required.
Advertisement
Join AiTechWorlds on Telegram and get daily AI tips, prompt engineering templates, coding resources, and exclusive content β 100% free!
No spam. Leave anytime.