Passkeys replace passwords with cryptographic keys stored on the user’s device. Learn how sign-up and sign-in work, why they resist phishing, and what to plan for.
Most account breaches begin with a password: guessed, reused on another site, or typed into a fake login page. Passkeys are designed to remove that whole category of problem, and support for them is now common across browsers, phones and password managers.
If you build or maintain a login system, it is worth understanding how they work, even if you add them later.
The idea in one paragraph
A passkey is a pair of cryptographic keys. The private key stays on the user’s device and never leaves it. The public key is stored on your server. To sign in, your server sends a random challenge, the device signs it with the private key after the user confirms with a fingerprint, face or device PIN, and your server checks the signature with the public key.
Your server never stores a secret that could be stolen and reused. A leaked database of public keys is useless to an attacker.
Why they resist phishing
A passkey is tied to the website’s domain. If a user lands on a look-alike site, the browser simply will not offer a passkey for it, because the domain does not match. There is no password to type into the wrong place. This is the biggest security gain over passwords and even over one-time codes sent by text.
What you need to build
Passkeys use the WebAuthn standard, which browsers expose through navigator.credentials. You rarely write the cryptography yourself. Use a well-maintained library for your language and focus on the product decisions.
- Registration. Ask the browser to create a credential, then store its credential ID and public key against the user.
- Sign-in. Generate a random challenge, ask the browser to sign it, and verify the response on the server.
- Recovery. Decide what happens when a user loses every device. Email links or backup codes are common.
- Multiple credentials. Let users add more than one passkey and remove old ones.
// Simplified. Your server supplies options (challenge, user, rpId).
const options = await fetch("/auth/register/options").then((r) => r.json());
const credential = await navigator.credentials.create({ publicKey: options });
await fetch("/auth/register/verify", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(credential), // helper libraries handle the encoding
});Things teams underestimate
- Account recovery is the real design problem. A weak recovery flow becomes the new way to attack the account.
- Cross-device use. Passkeys can sync through the platform account (such as the device maker’s password manager), and users can also use a phone to sign in on a computer.
- A gradual rollout. Most products offer passkeys next to passwords first, then nudge users to switch.
- Clear wording. Many people have never heard the word “passkey”. A short explanation on the screen helps adoption.