This episode opens the modern direction of 2FA: WebAuthn and passkeys, which are phishing-resistant without a shared secret. You compare TOTP, WebAuthn, and SMS, then design a multi-factor strategy that offers passkeys to new users while keeping TOTP for compatibility.

For 14 episodes you've been building TOTP — and that was the right decision for universal compatibility. Episode 17 lifts the head to look forward: WebAuthn and passkeys, the FIDO2 standard that changes how authentication works in 2026.
The fundamental difference isn't about digits and codes, but about the cryptographic model: TOTP shares one secret, WebAuthn never shares a secret at all. You'll understand that principle, compare the three approaches — TOTP, WebAuthn, and SMS — then design a strategy that offers both without confusing users.
WebAuthn replaces the shared secret with asymmetric cryptography. The user's device creates a key pair: the private key never leaves the device, the public key is stored on the server. At login, the server sends a random challenge, the device signs it with the private key, and the server verifies with the public key.
The consequence is big: a server database leak doesn't endanger anything — what leaks is only the public key, which is meant to be shared. And because there's no secret to copy, credential theft becomes far harder.
The WebAuthn login flow follows a challenge-response pattern:
1. The server creates a random challenge
2. The browser sends the challenge to the device
3. The device signs it with the private key
4. The server verifies with the public key
5. The signature is only valid for the origin domainThe flow above explains why phishing is powerless: a signature created for your domain is automatically rejected on another domain, even if the user enters the whole login flow into an imitation site.
A WebAuthn signature is only valid for one domain (origin). A code signed for bank.dev is automatically rejected by bank.dev.attacker.com — even if the user clicks a phishing link and completes the whole login flow. This is why WebAuthn is called phishing-resistant, while TOTP, which can be phished in real time, is not.
A passkey is a WebAuthn implementation that stores the key on a platform: iCloud Keychain for Apple, Google Password Manager, or Windows Hello. Keys sync across the same user's devices via the cloud, so the experience feels like a normal password — but with asymmetric security behind it.
An honest comparison of the three helps design decisions:
Method Phishing Resistant Offline Secret Confidentiality Sync
TOTP no yes shared (divided) device NTP
WebAuthn yes depends local private key vendor cloud
SMS OTP no no secret at carrier carrierTOTP excels at compatibility and vendor independence, but can be phished in real time. WebAuthn wins on phishing resistance with the consequence of trusting a vendor cloud for sync. SMS is the weakest — SIM swapping and carrier interception make it suitable only as recovery.
Read this table carefully: no row is perfect. Choosing a method isn't about finding the best in every column, but choosing the compromise that fits the application's context.
Start with TOTP as the universal default. Add WebAuthn for users who want maximum security and use modern platforms. Never make SMS the primary second factor — keep it for recovery or environments with no alternative.
A mature design offers a choice at enrollment: the user can scan the TOTP QR or create a passkey. Steer new users toward passkeys through recommendations and a prominent position, but keep TOTP one click away — not every user is comfortable with a key tied to a specific platform.
Passkey implementation can start with a library that handles the protocol:
npm install @simplewebauthn/server @simplewebauthn/browser@simplewebauthn/server handles creating and verifying the WebAuthn challenge server-side, while the browser version handles the client side. This library deals with the CBOR, attestation, and transport details that are complicated to implement manually.
One thing to remember: a passkey is tied to a specific device or cloud account. Make sure users know that switching devices means re-registering or syncing via the vendor's cloud account.
An important decision: passkeys add to, not replace, the TOTP you've already built. Users with old devices, unsupported browsers, or a preference for offline work are still served by TOTP. Secret rotation, recovery codes, and all the foundations from previous episodes remain relevant as a reliable fallback layer.
Introduce passkeys as an option, not a requirement. Watch the data: what percentage of users choose a passkey, and how many keep it after 30 days. Only then decide whether new users should be required to use a passkey once an established base forms.
Also monitor the passkey enrollment failure rate — if it's high, the problem is likely in the browser UX or device support, not user intent.
Note the implications of synced passkeys: losing access to the vendor cloud means losing the key — exactly like losing the TOTP phone. So recovery codes and the TOTP fallback remain a mandatory safety net, even for passkey users.
Show the status of registered methods on the security page, and let users revoke a lost device's passkey without deleting the account.
Episode 17 opened the modern direction of 2FA: phishing-resistant WebAuthn without a shared secret, an honest comparison of TOTP, WebAuthn, and SMS, and a strategy that offers passkeys to new users while keeping TOTP as the universal fallback.
The key takeaways:
In the next episode, episode 18, we will cover enterprise: enforcement and compliance — making MFA mandatory for admin and billing roles, a phase-in with 30-day notifications, per-tenant policies, and its relationship with SOC 2 and ISO 27001.