FIDO2 is phishing-resistant, hardware-backed, and uses public-key cryptography with no shared secrets – making it the stronger choice for high-security applications. TOTP is simpler to implement, requires no special hardware, and works with existing authenticator apps – making it better for broad compatibility and low-friction deployments. Choose FIDO2 for financial services, enterprise environments, or passwordless migration; choose TOTP when hardware authenticator adoption is impractical or you need legacy compatibility. Below is a full technical breakdown of how each protocol works, their security properties, and implementation code.
How TOTP Works
TOTP generates temporary passwords using a shared secret and the current time. The algorithm follows RFC 6238, which combines a secret key with the current Unix timestamp divided into 30-second intervals.
The core TOTP formula:
TOTP = HMAC-SHA1(secret, floor(current_unix_time / 30))
During setup, the server and client share a Base32-encoded secret. Each 30-second window produces a 6-digit code derived from the HMAC-SHA1 output. Google Authenticator, Authy, and password managers like Bitwarden implement this standard.
Here’s how you might validate a TOTP code server-side using Python:
import pyotp
def verify_totp(secret: str, code: str) -> bool:
totp = pyotp.TOTP(secret)
return totp.verify(code)
# Example usage
secret = "JBSWY3DPEHPK3PXP"
is_valid = verify_totp(secret, "123456")
TOTP codes are bound to time windows, meaning they’re valid for roughly 30-60 seconds. This temporal constraint limits replay attacks but also introduces usability challenges—users must enter codes quickly or request new ones.
How FIDO2 Works
FIDO2 (Fast Identity Online 2) is a broader standard comprising WebAuthn (the web API) and CTAP (Client to Authenticator Protocol). Instead of shared secrets, FIDO2 uses public-key cryptography with private keys stored on dedicated hardware or platform authenticators.
The authentication flow involves two phases:
- Registration: The server sends a challenge. The authenticator generates a key pair, signs the challenge with the private key, and returns the public key.
- Authentication: The server sends a new challenge. The authenticator signs it with the stored private key, proving possession without transmitting the key itself.
A WebAuthn registration request in JavaScript:
const publicKeyCredentialCreationOptions = {
challenge: new Uint8Array([21, 31, 105, 59, /* server challenge */]),
rp: {
name: "Your Service",
id: "yourdomain.com"
},
user: {
id: new Uint8Array([/* user ID bytes */]),
name: "username",
displayName: "User Name"
},
pubKeyCredParams: [
{ type: "public-key", alg: -7 }, // ES256
{ type: "public-key", alg: -257 } // RS256
],
authenticatorSelection: {
authenticatorAttachment: "platform",
userVerification: "preferred"
}
};
const credential = await navigator.credentials.create({
publicKey: publicKeyCredentialCreationOptions
});
FIDO2 authenticators include USB security keys (YubiKey, Solo), platform authenticators (Windows Hello, Touch ID, Face ID), and mobile device authenticators.
Security Comparison
TOTP Vulnerabilities
TOTP has known weaknesses that developers should understand:
- Phishing susceptibility: Users manually entering codes can be tricked into revealing them on fake login pages. Attackers capture these codes in real-time and use them before expiration.
- Secret storage: The shared secret must be stored securely on both server and client. Server-side breaches expose all secrets.
- No device verification: TOTP doesn’t verify the authentication device—it only verifies knowledge of the secret.
FIDO2 Security Properties
FIDO2 addresses these weaknesses through several mechanisms:
- Cryptographic binding: Private keys are never transmitted or stored on servers. Each service gets a unique public key.
- Phishing resistance: The authenticator verifies the relying party identity (RP ID) before signing. Keys are bound to specific domains and cannot be used on phishing sites.
- Hardware security: Private keys reside in secure elements or trusted platform modules (TPMs), making extraction extremely difficult.
- User verification: Platform authenticators require biometric confirmation or PIN entry, adding another authentication factor.
The security properties comparison:
| Property | TOTP | FIDO2 |
|---|---|---|
| Shared secret | Yes | No |
| Phishing resistant | Limited | Strong |
| Hardware-backed | No | Usually yes |
| Domain binding | No | Yes |
| Replay window | 30 seconds | None |
Implementation Considerations
When to Choose TOTP
TOTP remains appropriate for certain scenarios:
- Legacy compatibility: Many services still use TOTP, and users have existing authenticator apps.
- Low implementation complexity: Adding TOTP requires minimal infrastructure changes compared to FIDO2.
- No hardware requirements: Users only need a smartphone or computer with an authenticator app.
- Backup options: TOTP secrets can be stored in password managers, providing account recovery paths.
For developers, TOTP libraries exist in every major language. Implementation typically involves storing a secret, generating QR codes for setup, and validating 6-digit codes.
When to Choose FIDO2
FIDO2 is superior for high-security applications:
- Financial services: Banks and payment platforms benefit from hardware-backed authentication.
- Enterprise environments: Organizations requiring strong security baselines should consider FIDO2.
- Passwordless migration: FIDO2 enables true passwordless authentication, improving both security and UX.
WebAuthn support is now broad—Chrome, Firefox, Safari, and Edge all implement the standard. However, FIDO2 requires more server-side infrastructure, including challenge storage, public key management, and attestation verification.
A minimal FIDO2 verification server implementation in Python:
from webauthn import verify_registration_verify, verify_authentication_verify
from webauthn.helpers import bytes_to_base64url
def verify_login(authentication):
verification = verify_authentication_verify(
credential_id=authentication.credential_id,
client_data_json=authentication.client_data_json,
authenticator_data=authentication.authenticator_data,
signature=authentication.signature,
expected_challenge=authentication.challenge,
expected_origin="https://yourdomain.com",
expected_rp_id="yourdomain.com"
)
return verification.verified
Hybrid Approaches
Many modern services implement both TOTP and FIDO2, allowing users to choose their preferred method. Administrators might require FIDO2 for privileged accounts while offering TOTP as a fallback for users without hardware authenticators.
Progressive enrollment strategies work well: start users with TOTP, then prompt them to add FIDO2 credentials once familiar with the system. This reduces support burden while improving security for willing users.
Conclusion
TOTP and FIDO2 serve different security needs. TOTP offers simplicity and broad compatibility at the cost of phishing susceptibility and shared-secret architecture. FIDO2 provides stronger security through hardware-backed keys and domain binding, but requires more implementation effort and user hardware.
For developers building new systems, FIDO2 should be the default choice when possible. Reserve TOTP for compatibility requirements or situations where hardware authenticator adoption is impractical. The extra implementation complexity pays dividends in security posture and user trust.
Related Reading
- Bitwarden Vault Export Backup Guide: Complete Technical.
- Telegram vs Signal: Which Is Actually Safer? A Technical.
- GDPR Joint Controller Agreement Template: A Developer Guide
Built by theluckystrike — More at zovo.one