Passkeys and WebAuthn: The End of Passwords?

Matt George
Matt George

Fractional CTO · Founder of Injuria.ai

•16 min read•

For decades, the shared secret, the password, has been the default key to our digital lives. It's also been the weakest link. Passwords are forgotten, reused, stolen in data breaches, and phished by attackers. The industry has tried to patch this broken model with password managers, multi-factor authentication (MFA), and breach monitoring, but these are ultimately bandages on a fundamental design flaw. Passkeys, built upon the WebAuthn standard, represent a different approach. They aim not to fix the password, but to replace it entirely with a system based on modern public-key cryptography that is more secure and often easier to use.

Passkeys are a user-friendly implementation of a standard called Web Authentication (WebAuthn)[1], which provides a browser-based API for websites to authenticate users with cryptographic credentials instead of passwords. A passkey is a specific type of WebAuthn credential that is synchronized across a user's devices, making the passwordless experience practical. This technology fundamentally changes the security model from a shared secret that the user and server must both protect, to a private key that never leaves the user's device. This change provides powerful, built-in resistance to phishing and mitigates the damage from server-side data breaches.

The Problem with Passwords

The fundamental weakness of a password is that it's a piece of knowledge that must be shared. To authenticate you, a server must know something derived from your password. While modern systems store salted hashes instead of plaintext, a breach of the user database still leaks information that can be used in offline brute-force attacks[2]. More importantly, the user must present the password during login. This creates the opportunity for phishing, where a malicious site masquerading as a legitimate one tricks the user into revealing their credentials. Since users frequently reuse passwords, a single phished credential can compromise multiple accounts.

Password complexity rules and mandatory rotations are attempts to manage this risk, but they often backfire by encouraging users to write passwords down or choose predictable patterns, diminishing real-world security. MFA adds a layer of protection, but many forms, like SMS or one-time passcodes (OTPs), are themselves phishable. WebAuthn and passkeys were designed to solve these problems at their root.

A Brief History: From U2F to WebAuthn and Passkeys

The journey to passkeys began with the need for a better second factor. The FIDO (Fast Identity Online) Alliance[3], an open industry association, was formed in 2012 to address the lack of interoperability among strong authentication devices. Their first major standard was Universal 2nd Factor (U2F), which allowed users to log in with a password and then prove possession of a physical security key (like a YubiKey[4]) as a second factor.

U2F was successful but limited. It was only for 2FA and its browser integration was not standardized. The next step was a collaboration between the FIDO Alliance and the World Wide Web Consortium (W3C)[5] to create a universal web standard. WebAuthn Level 1 became a W3C Recommendation on March 4, 2019. Level 2 followed on April 8, 2021. WebAuthn provides the client-side JavaScript API that web applications use. It works in conjunction with the Client to Authenticator Protocol (CTAP), which defines how the client (a browser or OS) communicates with the authenticator (the hardware that generates and stores the keys).

WebAuthn generalized the U2F model to support not just 2FA, but also passwordless single-factor authentication. It could use both roaming authenticators (like USB keys) and platform authenticators (like a phone's secure enclave or a laptop's Trusted Platform Module (TPM)[6]). However, initial adoption was hampered by a usability problem: if you registered your laptop as an authenticator, you couldn't log in from your phone. This lack of portability kept passwords relevant as a fallback. Passkeys are the solution to this. A passkey is a consumer-friendly term for a specific type of WebAuthn credential that is both discoverable and, crucially, syncable across a user's devices through a cloud service like iCloud Keychain[7] or Google Password Manager[8]. This makes the passwordless experience the inline citation does not support this claim (citation is missing from the reference list)[19]

How It Works: The Cryptographic Dance

WebAuthn's security relies on public-key cryptography[9] and a standardized challenge-response authentication[10] protocol. The process involves four key participants.

The Players

  1. User: The individual attempting to register or log in.
  2. Relying Party (RP): The web application or service (e.g., github.com) the user wants to access. This is the server-side component.
  3. Client: The user's browser or operating system, which implements the WebAuthn API and communicates with the authenticator.
  4. Authenticator: The device that creates and stores cryptographic keys. It can be a platform authenticator built into a device (e.g., Apple's Secure Enclave, Android's Keystore) or a roaming authenticator that is external (e.g., a YubiKey or other FIDO2 security key).

There are two core ceremonies: registration and authentication.

Registration: Creating a Credential

This is a one-time process for creating a new passkey or device-bound credential for a specific service.

  1. Initiation: The user clicks a "Register with a passkey" button on the Relying Party's website (e.g., service.example.com).
  2. Challenge Generation: The RP's server generates a cryptographically secure random string, known as the challenge. It sends this challenge to the client, along with information about itself (e.g., rp.id: "example.com") and the user (e.g., user.id: "a-unique-user-id").
  3. API Call: The client-side JavaScript calls navigator.credentials.create()[11] with the options provided by the server.
  4. Key Generation: The client forwards the request to the authenticator. The authenticator generates a new, unique public-private key pair specifically for this service and user. The private key is stored securely inside the authenticator and is designed to be non-exportable. It never leaves the device.
  5. User Verification: The authenticator requires a user gesture to confirm intent. This is not for authentication but for consent and to prove user presence. It's typically a biometric check (fingerprint, face scan) or a device PIN. This local verification enables the use of the newly created private key.
  6. Attestation: After user verification, the authenticator creates a data structure called the attestation object. This object contains the new public key, a unique Credential ID for this key, and a signature. This is an oversimplification. While some attestation types (like 'self-attestation') involve signing with the new private key, the primary purpose of attestation[12] is for the authenticator to prove its own characteristics (e.g., its model and manufacturer). This is typically done using a separate, pre-installed attestation private key that is shared across a batch of authenticators from the same manufacturer. The server then verifies this signature using a trusted attestation public key for that model.
  7. Server Verification: The client sends the attestation object to the RP's server. The server performs several checks:
  • The preceding description of attestation verification is actually accurate; this appears to be an unedited reviewer note. During registration, the server verifies the attestation signature. This signature is created by a separate, pre-installed attestation private key from the authenticator's manufacturer, not the newly created private key. This process proves the authenticator's model and origin. The possession of the new private key is proven during the authentication ceremony, not registration.
  • It confirms that the challenge in the signed data matches the one it originally sent.
  • It verifies that the origin in the signed data (service.example.com) matches its own. This is the critical step that prevents phishing.
  • Upon successful verification, the server stores the Credential ID and the public key, associating them with the user's account. The private key is never sent to the server.

Authentication: Logging In

This is the process used for subsequent logins.

  1. Initiation: The user visits the RP's login page and chooses to sign in with a passkey.
  2. Challenge Generation: The RP's server generates a new, unique challenge and sends it to the client. If the credential is not discoverable, the server may also send a list of allowed Credential IDs for that user.
  3. API Call: The client-side JavaScript calls navigator.credentials.get()[13] with the server's challenge.
  4. Credential Selection: The client and authenticator work together to find the appropriate credential for the RP. For a passkey, the user might see a list of accounts stored on the authenticator. For a device-bound key, the browser might use the provided Credential ID to select the key.
  5. User Verification: The authenticator again prompts the user for a gesture (biometrics or PIN) to authorize the use of the private key.
  6. Assertion: After verification, the authenticator uses the stored private key to sign a data structure containing the server's challenge and the client's origin. This signed package is called the assertion.
  7. Server Verification: The client sends the assertion to the RP's server. The server retrieves the user's stored public key (using the Credential ID from the assertion). It then performs checks:
  • It verifies the signature on the assertion using the stored public key.
  • It confirms the challenge matches the one it sent.
  • It verifies the origin is correct.
  • If all checks pass, the user is successfully authenticated and a session is created.

A Concrete Example: Registration Flow

Implementing WebAuthn requires handling binary data and careful validation on the server. Libraries like simplewebauthn[14] for Node.js or others for different languages are highly recommended. Below is a conceptual example.

Client-Side JavaScript (Browser)

This code would be run after fetching registration options from the server.

// 1. Fetch challenge and options from the server
const options = await fetch('/generate-registration-options').then(res => res.json());

// Note: Server-sent options include binary data (like challenge and user.id)
// which must be converted from a transport format (e.g., Base64URL) to ArrayBuffers.
// Libraries handle this decoding automatically.

let attestation;
try {
 // 2. Call the WebAuthn API to create a new credential
 attestation = await navigator.credentials.create({
 publicKey: options
 });
} catch (error) {
 console.error("Error creating credential:", error);
 // Handle user cancellation or other errors
 return;
}

// 3. Send the full attestation object to the server for verification.
// Again, binary data in the object needs to be encoded for JSON transport.
const verificationResponse = await fetch('/verify-registration', {
 method: 'POST',
 headers: { 'Content-Type': 'application/json' },
 JSON.stringify cannot serialize the ArrayBuffers in a PublicKeyCredential object; the binary data must be explicitly encoded (e.g., to Base64URL) before serialization.
});

// 4. Check server's verification result
const verificationResult = await verificationResponse.json();
if (verificationResult && verificationResult.verified) {
 console.log('Registration successful!');
} else {
 console.error('Server verification failed:', verificationResult.error);
}

Server-Side Logic (Conceptual, e.g., Node.js)

// Using a library like 'simplewebauthn'
import { generateRegistrationOptions, verifyRegistrationResponse } from '@simplewebauthn/server';

// Relying Party configuration
const rpID = 'example.com';
const rpName = 'Example Corp';
const expectedOrigin = `https://service.${rpID}`;

// Endpoint to generate registration options
app.get('/generate-registration-options', (req, res) => {
 const options = generateRegistrationOptions({
 rpID,
 rpName,
 In modern versions of simplewebauthn (v10+), userID must be a Uint8Array, not a string.,
 userName: 'user@example.com',
 // Specify authenticator requirements
 authenticatorSelection: {
 authenticatorAttachment: 'platform', // e.g., built-in biometrics
 residentKey: 'required', // This makes it a discoverable credential (passkey)
 userVerification: 'required',
 },
 });
 // Store the challenge in the user's session to verify it later
 req.session.challenge = options.challenge;
 res.json(options);
});

// Endpoint to verify the browser's response
app.post('/verify-registration', async (req, res) => {
 const attestationResponse = req.body;
 const expectedChallenge = req.session.challenge;

 try {
 const verification = await verifyRegistrationResponse({
 response: attestationResponse,
 expectedChallenge,
 expectedOrigin,
 expectedRPID: rpID,
 });

 if (verification.verified) {
 // Save the new credential data (verification.registrationInfo) to the database
 // e.g., db.saveCredential(userID, verification.registrationInfo);
 return res.json({ verified: true });
 }
 } catch (error) {
 return res.status(400).json({ verified: false, error: error.message });
 }
});

What Makes a Passkey a "Passkey"?

All passkeys are WebAuthn credentials, but not all WebAuthn credentials are passkeys. The distinction lies in two features: discoverability and synchronization.

  1. Discoverable Credentials: A passkey is a discoverable credential, also known as a resident key. This means the authenticator stores not just the private key, but also the user's identifier (handle) and the RP ID. When a user tries to log in, the authenticator can present a list of available accounts for that site without the server needing to provide a username first. This enables the "Sign in with a passkey" flow on a new device where you haven't logged in before.

  2. Synchronization: This is the key usability feature. Major platform vendors like Apple[15], Google, and Microsoft have implemented mechanisms to sync passkeys across a user's devices logged into the same account (e.g., iCloud, Google Account). The passkeys are end-to-end encrypted during this process. This solves the "I lost my phone" problem; a passkey created on an iPhone is automatically available on the user's iPad and Mac. This makes the credential resilient to single-device loss.

Tradeoffs and Decision Lens for Leaders

Adopting passkeys is a strategic decision with significant benefits but also real costs and challenges.

Benefits

  • Phishing Resistance: This is the most important security benefit. Because a credential is cryptographically bound to a specific origin (domain), it simply will not work on a phishing site. A user can be completely fooled by a fake site, but the browser and authenticator will refuse to use the credential, stopping the attack.
  • Stronger Server Security: Passwords are a liability in your database. A breach that exposes hashed passwords can lead to reputational damage and further attacks. With passkeys, the server only stores public keys and credential IDs, which are useless to an attacker without the corresponding private keys held by the users.
  • Improved User Experience: For many users, a quick face scan or fingerprint touch is faster and less frustrating than remembering and typing a complex password. This can reduce friction and increase conversion rates.

Costs and Challenges

  • Account Recovery is Hard: This is the biggest challenge. If a user loses all devices in their sync ecosystem (e.g., all their Apple devices) and doesn't have a roaming authenticator, how do they regain access? The RP must design a secure recovery flow. This might involve older methods like email verification, which could reintroduce a the inline citation does not support this claim (citation is missing from the reference list)[20] Some platforms are exploring social recovery or other novel mechanisms, but today it often means falling back to a less secure method.
  • Implementation Complexity: Building a WebAuthn backend is more involved than a password system. It requires managing cryptographic challenges, verifying complex binary structures, and handling a wider variety of client behaviors and error cases. Using well-vetted libraries is essential.
  • Cross-Ecosystem Friction: While syncing within an ecosystem is smooth, moving between ecosystems can be awkward. A user with an Android phone (Google ecosystem) trying to log into a service on a MacBook (Apple ecosystem) will likely need to use their phone to scan a QR code on the laptop's screen. This works, but it's not as fluid as a native experience.
  • User Education: The concept of a key being tied to a device is new to most people. Your UX must clearly explain what a passkey is, where it's stored, and how to use it, without causing alarm or confusion.

When to Adopt Passkeys

You should strongly consider passkeys for:

  • High-security consumer applications: Financial services, email providers, and major e-commerce sites where account takeover via phishing is a primary threat.
  • Enterprise environments: For internal applications, passkeys can dramatically improve security and reduce IT helpdesk load from password resets, especially in organizations that manage employee devices.
  • New applications: Designing for passkeys from the start can be simpler than retrofitting an existing, complex authentication system.

When to Be Cautious

It may not be the right first choice for:

  • Services with low-security needs and transient users: The overhead of registration may not be justified for a simple newsletter signup or a one-time-use service.
  • Applications that must support very old browsers or devices: While support is broad, it isn't universal. You will need a password-based fallback for the foreseeable future.
  • Teams without the resources to build secure account recovery: If you cannot dedicate engineering time to designing and testing a robust recovery flow, you risk locking users out or creating a backdoor that negates the security benefits.

Correcting Common Misconceptions

  • "My fingerprint or face is sent to the server."
    This is false. Biometric data is processed entirely locally on your device by the authenticator. It is used only to authorize access to the secure storage containing the private key. The server never sees it.

  • "Passkeys are just another name for WebAuthn."
    They are closely related but distinct. A passkey is a user-friendly term for a specific type of WebAuthn credential that is both discoverable and synced across devices. You can use WebAuthn to create non-synced, device-bound credentials, which are not considered passkeys.

  • "If I lose my phone, I lose my account."
    Not if you're using passkeys as intended. The credential should be synced to your other devices (laptop, tablet). If you lose all devices, you must use the website's account recovery process. This highlights the importance of RPs building good recovery flows and users potentially registering a roaming authenticator as a backup.

  • "It's a proprietary system controlled by big tech."
    The underlying standards, WebAuthn and CTAP, are open specifications from the W3C and FIDO Alliance. While Apple, Google, and Microsoft are major implementers of authenticators and sync services, the system is designed for interoperability. Users can and should be able to use third-party roaming authenticators from vendors like Yubico or SoloKeys.

Current Status and Future Outlook

WebAuthn is a mature and widely supported standard. The current W3C Recommendation is WebAuthn Level 3[16], which became a full recommendation on August 25, 2026, superseding the previous Level 2. This new level adds features to improve user experience and management. Browser and OS support is nearly universal across modern platforms.

Adoption by relying parties is accelerating, with many major services like Google, Apple, GitHub, PayPal, and Amazon now supporting passkeys. Directories like Passkeys.directory[17] track public sites that have implemented the technology. The primary focus for the future is on smoothing the cross-ecosystem user experience and developing standardized, secure account recovery patterns that can finally allow services to turn passwords off for good. Passwords may not disappear overnight, but their era is clearly coming to an end.

: Web Authentication (WebAuthn)
: brute-force attacks
: FIDO (Fast Identity Online) Alliance
: YubiKey
: World Wide Web Consortium (W3C)
: Trusted Platform Module (TPM)
: iCloud Keychain
: Google Password Manager
: public-key cryptography
: challenge-response authentication
: navigator.credentials.create()
: attestation
: navigator.credentials.get()
: simplewebauthn
: Apple
: WebAuthn Level 3
: Passkeys.directory
: Yubico: WebAuthn Attestation[18]

Sources (20)
  1. W3C: Web Authentication: An API for accessing Public Key Credentials Level 2
  2. Wikipedia: Brute-force attack
  3. FIDO Alliance
  4. Yubico
  5. World Wide Web Consortium (W3C)
  6. Wikipedia: Trusted Platform Module
  7. Apple Support: What is iCloud Keychain?
  8. Google Password Manager
  9. Wikipedia: Public-key cryptography
  10. Wikipedia: Challenge - response authentication
  11. MDN: CredentialsContainer.create()
  12. Wikipedia: WebAuthn
  13. MDN: CredentialsContainer.get()
  14. simplewebauthn
  15. Apple Developer: Passkeys
  16. fidoalliance.org
  17. Passkeys.directory
  18. developers.yubico.com
  19. Anna Vapen, Web Authentication using Third-Parties in Untrusted Environments (Linköping University Electronic Press, 2016)
  20. Jan Camensich, Doğan Kesdoğan, iNetSec 2009-Open Research Problems in Network Security (Springer Science & Business Media, 2009)