Google has over 800 million accounts using passkeys. Amazon saw 175 million users create one in the first year. 26% of all sign-ins across major platforms are now completed with a passkey. The technology is past the early-adopter phase. Whether your app should be using it depends on your user base and how your auth is currently structured.
What a passkey actually is
A passkey is a cryptographic key pair. The private key lives on your device, inside the secure enclave or platform authenticator (Touch ID, Face ID, Windows Hello, or a hardware security key). The public key is stored on the server. To authenticate, you prove you have the private key by signing a challenge with it. The server verifies the signature against the stored public key.
The private key never leaves your device. There is nothing to steal from the server because only the public key is there. There is nothing to phish because the authentication is bound to the specific domain it was created for: a phishing site on a different domain cannot receive a valid passkey response.
Compare that to passwords: the server stores a hash of your password. If the server is breached, the hashes are exposed. Weak hashes get cracked. Strong hashes take time but can eventually be cracked. Phishing sites steal your password in real time. None of those problems exist with passkeys.
The actual adoption numbers
The FIDO Alliance Passkey Index shows 93% of user accounts across major platforms are now eligible for passkey sign-in. 36% of eligible accounts have enrolled a passkey. Passkey authentication is 30% faster than password-plus-2FA flows, which is why companies with conversion-sensitive products have been aggressive adopters.
Nearly all major auth providers now support passkeys: Clerk, Auth0, Cognito, Supabase Auth, and Firebase Authentication all added passkey support in 2024-2025. If you are building a new app today and using one of those providers, you can add passkeys in a few hours.
When passkeys are worth adding
Passkeys make the most sense for consumer-facing apps where account takeover is a real business risk: anything that holds financial data, personal information, or access to things with real-world value. Banking apps, healthcare portals, e-commerce accounts. The combination of phishing resistance and faster login makes passkeys a genuine UX improvement for those users, not just a security upgrade.
They also make sense for new apps where you can design for them from the start. Retrofitting passkeys onto an existing password system is more work than building in the support from day one. If you are building something new and already planning to require 2FA, passkeys replace the password and the second factor in a single flow. That is a meaningful simplification.
Where passkeys are less compelling: B2B internal tools where everyone already has SSO through Microsoft or Google, and the overhead of adding another auth mechanism is not worth the benefit. Also apps where users share devices without separate OS accounts, which makes passkey enrollment confusing. The technology is not universal yet. Device compatibility is broad but not complete.
How to implement passkeys
The underlying standard is WebAuthn. You can implement it directly, but for most apps the right answer is to use an auth provider that handles it for you.
Option 1: Use an auth provider (recommended)
With Clerk, adding passkeys is a configuration change plus a UI component:
// 1. Enable passkeys in Clerk dashboard under User & Authentication -> Authentication
// 2. Add the passkey management UI to your account settings page:
import { UserProfile } from "@clerk/nextjs";
export default function AccountSettings() {
return <UserProfile />; // Passkey management is included automatically
}
// 3. Users can now add a passkey from their profile and sign in with itOption 2: Raw WebAuthn (when you need full control)
If you manage your own auth, the SimpleWebAuthn library handles the WebAuthn complexity on both server and client:
// Server: generate registration options
import { generateRegistrationOptions } from '@simplewebauthn/server';
const options = await generateRegistrationOptions({
rpName: 'Your App Name',
rpID: 'yourdomain.com',
userID: userId,
userName: userEmail,
attestationType: 'none',
authenticatorSelection: {
residentKey: 'required', // enables passkey (discoverable credential)
userVerification: 'required', // requires biometric/PIN
},
});
// Store options.challenge in session for verification
// Return options to client
// Client: trigger the browser passkey prompt
import { startRegistration } from '@simplewebauthn/browser';
const credential = await startRegistration(options);
// Send credential to server for verificationWhat about existing password users?
The practical approach: offer passkeys as an upgrade, not a hard cut. Let existing users log in with their password, then prompt them to add a passkey for faster future logins. Over time, as passkey adoption grows among your users, you can make them the default and deprecate password-only logins.
Do not force passkeys immediately. Users who do not have a compatible device, or who use multiple devices, need a fallback. Email magic links or TOTP are good fallbacks. Plain passwords alone are the worst fallback because they reintroduce phishing risk.
The password + bcrypt baseline
If you are not ready for passkeys yet, at minimum: bcrypt or Argon2 for password hashing, no storing plaintext or MD5/SHA1 hashes, MFA required for admin accounts, and rate limiting on login endpoints. These are table stakes in 2026. Passkeys are the upgrade path, but a correct password implementation is still the minimum bar.
$ build --auth-correctly
Whether you need passkeys, a solid JWT setup, or just want someone to look at what you've already got, I handle auth as part of backend work.
$ ./start-backend-project.sh →