implementation in detail from a security perspective
Passwords share one structural flaw: the server and the user hold the same secret, and every place that secret ever gets typed or stored becomes a potential attack surface. WebAuthn solves this at the root by replacing shared secrets entirely with asymmetric public-key cryptography. Understanding why passkeys are not just more convenient but structurally more secure than passwords requires walking through the registration and authentication ceremony in detail.
Table of Contents
- 1. The core principle: public-key cryptography instead of shared secrets
- 2. The registration ceremony in detail
- 3. The authentication ceremony in detail
- 4. Phishing resistance as the central security advantage
- 5. Discoverable credentials: the difference between WebAuthn and true passkeys
- 6. Conditional UI: blending passkeys into existing login forms
- 7. Fallback strategy for users without a WebAuthn-capable device
- 8. Critical server-side implementation details
- 9. Conclusion: WebAuthn as a structurally superior authentication approach
- 10. Summary
- 11. FAQ
1. The core principle: public-key cryptography instead of shared secrets
In a classic password-based login, both the user and the server know the same secret, with the server ideally storing only a hash of it. Even so, the plaintext password has to be transmitted on every single login attempt, opening up attack surfaces like phishing pages, compromised network paths, or insecure storage in the browser.
WebAuthn works fundamentally differently: during registration, the user's device generates a cryptographic key pair consisting of a private and a public key. The private key never leaves the device or its security chip, while the public key is transmitted to and stored on the server. On every authentication, the device signs a server-issued challenge with the private key, and the server verifies that signature against the stored public key, without a secret value ever leaving the device.
// Registration ceremony: creating a PublicKeyCredential in the browser
async function registerPasskey(challengeFromServer, userId, userName) {
const publicKeyOptions = {
challenge: challengeFromServer, // Uint8Array, generated per request by the server
rp: { name: 'Example Shop', id: 'example-shop.com' },
user: {
id: userId, // Uint8Array, stable server-side user ID
name: userName, // e.g. email address
displayName: userName,
},
pubKeyCredParams: [
{ type: 'public-key', alg: -7 }, // ES256
{ type: 'public-key', alg: -257 }, // RS256 as a fallback
],
authenticatorSelection: {
userVerification: 'required', // PIN/biometric required
residentKey: 'required', // discoverable credential for passkey
},
timeout: 60000,
attestation: 'none',
};
const credential = await navigator.credentials.create({ publicKey: publicKeyOptions });
// credential.response holds the public key -- send it to the server
return credential;
}
2. The registration ceremony in detail
The registration flow, called a ceremony in the WebAuthn specification, starts with the server generating a random, sufficiently long challenge and sending it to the browser, along with information about the relying party, meaning the service itself, and the logged-in user. The browser passes this data to the authenticator through the navigator.credentials.create() API, meaning either a hardware security key, a platform's built-in security chip on a phone or laptop, or a passkey cloud sync provider.
The authenticator requests local verification from the user, for example a fingerprint, face recognition, or a device PIN, then generates a fresh key pair scoped specifically to this relying party and signs an attestation proving the public key genuinely came from a real authenticator. The browser sends the public key, the signed challenge, and metadata such as the credential ID back to the server, which, after verification, stores this data permanently linked to the user account.
3. The authentication ceremony in detail
For an actual login, the server again generates a fresh, random challenge and sends it to the browser along with the list of the user's known credential IDs. The navigator.credentials.get() API passes this challenge to the authenticator, which identifies the matching key pair generated during registration by its credential ID.
After another round of local user verification, the authenticator signs the challenge together with additional security-relevant data, such as the actual origin of the calling page, using the private key, and sends the signature back to the browser. The server verifies that signature against the public key stored at registration time along with the origin claim, and grants access on success, without ever transmitting a reusable secret.
4. Phishing resistance as the central security advantage
The decisive security gain over passwords, and even over one-time-code-based second factors, lies in a WebAuthn credential being structurally bound to the exact origin of the site it was registered on. On every authentication, the browser automatically passes the calling page's actual origin to the authenticator, which refuses to sign the challenge the moment that origin does not match the one recorded at registration.
If a user visits a phishing page under a similar but distinct domain, say example-shop-login.com instead of example-shop.com, the authenticator detects the origin mismatch automatically and refuses any interaction, without the user ever having to carefully scrutinize the domain themselves. This mechanism differs fundamentally from SMS or authenticator-app one-time codes, which a user can accidentally type into a phishing page as well, because the codes themselves carry no origin binding whatsoever.
5. Discoverable credentials: the difference between WebAuthn and true passkeys
While WebAuthn has existed as a web API for years, the newer term passkey specifically refers to so-called discoverable credentials, where the reference to the key pair lives directly on the authenticator instead of needing to be sent by the server on every login. This enables a login flow where the user does not even need to type a username, since the browser can directly surface matching, locally stored credentials.
Modern operating systems such as iOS, Android, and Windows additionally support cloud syncing of these discoverable credentials through the respective platform account, so a passkey registered once becomes automatically available on every device tied to the same user. This syncing solves the earlier core problem of hardware security keys, namely losing access after losing the physical device, without weakening the cryptographic security of the underlying mechanism.
6. Conditional UI: blending passkeys into existing login forms
A particularly user-friendly extension of WebAuthn is conditional UI, where an existing login form with a classic username and password field stays exactly as it is, but the browser additionally surfaces matching, locally stored passkeys directly in the username field's autofill dropdown. The user can then either type a password or log in with one click on the suggested passkey via biometrics, without the page needing two separate forms or buttons.
Technically, conditional UI is enabled through the autocomplete="username webauthn" attribute on the input field along with the mediation: 'conditional' option when calling navigator.credentials.get(), with the request already issued in the background as the page loads and only resolved once the user actually interacts with the autofill suggestion. This approach makes gradually migrating existing password forms considerably easier, since no structural change to the visible form is required.
7. Fallback strategy for users without a WebAuthn-capable device
Despite broad support across modern operating systems and browsers, there are still users with older devices, restrictive corporate environments, or specific accessibility needs who cannot use WebAuthn. A production-ready system therefore needs a fallback path, one that must not itself become the weakest link in the security chain.
A well-proven fallback is a password combined with an additional second factor, such as a TOTP app, rather than plain password login without extra protection. It is equally important not to quietly make the fallback path easier than the WebAuthn path, since an attacker will deliberately target whichever of the two paths is weaker. Some systems therefore offer WebAuthn as a mandatory second factor on top of the password rather than treating it as a full password replacement, until passkey adoption reaches a sufficiently high level.
8. Critical server-side implementation details
Server-side, every challenge must be random, sufficiently long, and valid for exactly one authentication attempt, typically stored short-lived in a server-side session and invalidated immediately after use. Origin verification must never be left to the client; it has to happen server-side against the client data signed by the authenticator, since a purely client-side check can be trivially bypassed by an attacker.
Equally important is maintaining a signature counter per credential, which should return an increasing value on every successful authentication. A stagnant or decreasing counter value can indicate a cloned credential, for example from a compromised hardware authenticator, and should be treated as a warning sign server-side, though modern cloud-synced passkey implementations sometimes no longer increment this counter reliably.
9. Conclusion: WebAuthn as a structurally superior authentication approach
WebAuthn does not fix the core problem of password-based systems through extra rules or complexity requirements, but through a fundamentally different cryptographic approach, one where a reusable secret never leaves the user's device. The tight binding to the actual origin makes phishing structurally ineffective, instead of relying solely on user vigilance the way passwords and one-time codes both do.
For a production-ready implementation, what matters beyond the ceremony logic itself is server-side rigor in challenge generation and origin verification, plus a well-designed fallback strategy for users without a compatible device that is not allowed to be weaker. Teams that implement these pieces cleanly reach a security level that classic passwords, even under the strictest password policy, practically cannot match.
| Aspect | Password | One-time code (SMS/app) | WebAuthn/passkey |
|---|---|---|---|
| Secret stored on server | Hash stored | No persistent secret | Public key only |
| Phishing resistance | None | Low, code can be typed manually anywhere | Structural, bound to origin |
| Reusability if stolen | High | Time-limited | None, signature unique per challenge |
| User friction | Recall and type required | Extra step required | Biometric/PIN, often one tap |
| Device loss | No direct risk | No direct risk | Cloud sync significantly reduces risk |
Mironsoft
Security audits, OWASP-compliant hardening, and secure architecture
Applications that actually hold up against a real attack attempt?
We review existing applications for classic OWASP vulnerabilities, insecure authentication, and missing input validation, then build an architecture that structurally reduces attack surface instead of just patching individual symptoms.
Security Audit
Systematically checking OWASP Top 10, auth flows, and input validation for vulnerabilities.
Secure Architecture
Building rate limiting, encryption, and access controls correctly from the ground up.
Incident Readiness
Establishing logging, monitoring, and response processes for when things go wrong.
10. Summary
WebAuthn/Passkeys implementation at a glance
Core principle
The private key never leaves the device; only the public key gets shared.
Ceremony
Registration generates a key pair; authentication signs a fresh server challenge.
Security advantage
Origin binding makes phishing pages structurally ineffective.
In practice
The fallback for users without a WebAuthn device must not be weaker.