Passkeys at the Protocol Layer: Eliminating Passwords with Asymmetric Authentication
A Technical Deep Dive into WebAuthn, FIDO2, and the Cryptographic Architecture Powering Passkeys
🔐 Passkeys: Protocol-Level Deep Dive and Usage Guide
Passwords are an implementation bug. For decades, they’ve served as brittle shared secrets, vulnerable to phishing, credential stuffing, and exfiltration. Passkeys represent a long-overdue protocol-level fix. They completely remove passwords and use pairs of keys instead, which are stored safely in hardware or a secure area and are only meant for a specific service.
This write-up walks through the historical evolution, WebAuthn-based protocol stack, and step-by-step implementation details of passkeys—for developers and system architects who care about correctness at the protocol layer.
🕰️ Historical Context
Legacy Authentication:
Shared secrets between client and server
Stored in central databases → breachable
Transmitted via POST forms → phishable
Initial Fixes:
MFA (TOTP, SMS)—adds latency, still phishable
OAuth/OIDC—offloads identity to IDPs, introduces complexity
Hardware keys (U2F/FIDO)—cryptographically sound, but UX friction
Modern Shift:
FIDO2 + WebAuthn—an asymmetric challenge–response protocol anchored in hardware-backed keys
Passkeys—UX abstraction over FIDO credentials, with cross-device availability and cloud sync
📜 Protocol Stack Overview
1. FIDO2
FIDO2 is a two-part standard:
CTAP2 (Client to Authenticator Protocol)—for interaction between browser/OS and the authenticator
WebAuthn—for browser ↔️ web application communication
2. Passkeys
Passkeys are discoverable WebAuthn credentials, meaning they:
Can be listed without the server knowing their ID ahead of time
Are scoped per origin (
RP ID)Are backed by asymmetric key pairs stored in secure enclaves (e.g., Secure Enclave, Android Keystore, TPM)
Are synced across devices using E2EE (iCloud Keychain, Google Password Manager)
🧬 Cryptographic Foundation
Each passkey consists of:
A public key (stored by the relying party)
A private key (held securely by the client-side authenticator)
An attestation format (optional metadata about the authenticator)
An RP ID hash, used to prevent cross-origin leaks
Authentication is a challenge-response protocol, not a credential lookup.
🏗️ Registration Flow: Step-by-Step
At the wire protocol level, a passkey registration event proceeds as follows:
Step 1: Server Generates a Challenge
The server issues a navigator.credentials.create()request via JavaScript:
const publicKeyCredentialCreationOptions = {
challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))),
rp: { name: "MyApp", id: "example.com" },
user: {
id: Uint8Array.from("user-unique-id", c => c.charCodeAt(0)),
name: "alice@example.com",
displayName: "Alice"
},
pubKeyCredParams: [{ type: "public-key", alg: -7 }],
authenticatorSelection: {
residentKey: "required",
userVerification: "preferred"
},
timeout: 60000,
attestation: "none"
};Step 2: Client Creates Keypair
The platform authenticator (Secure Enclave, Android Keystore, TPM) generates a new keypair.
The private key is non-exportable and scoped to the RP ID
The server receives the public key, credential ID, and attestation data.
Step 3: Server Verifies Attestation
The RP validates the challenge match, origin binding, and attestation (if enabled).
The system stores the credential ID and public key, which are then bound to the user's identity.
Note: This credential is now a passkey if discoverable and synced via cloud keychains.
🔐 Authentication Flow: Step-by-Step
Logging in with a passkey initiates a WebAuthn assertion request:
Step 1: Server Sends Challenge
const publicKeyCredentialRequestOptions = {
challenge: Uint8Array.from(window.crypto.getRandomValues(new Uint8Array(32))),
rpId: "example.com",
timeout: 60000,
userVerification: "preferred"
};Step 2: Client Signs Challenge
The browser invokes the authenticator.
A private key is used to sign a structure that consists of:
clientDataJSON(origin, challenge, type)authenticatorData(RP ID hash, flags, signature counter)
Returns:
signatureuserHandle(if resident)authenticatorDataclientDataJSONcredential ID
Step 3: Server Verifies Response
Confirms the signature using the stored public key
Validates:
Challenge integrity
RP ID hash matches expected domain
User verification flag set
The signature counter is strictly increasing (anti-replay)
Authentication completes. The system never sent a password.
🔁 Synced Passkeys (Cloud Sync via E2EE)
In the case of synced passkeys, the authenticator is abstracted across multiple devices via encrypted keychain sync:
Apple: iCloud Keychain + Secure Enclave
Google: Android Keystore + Password Manager
Under the hood:
The private key is backed by secure hardware per device.
The key material is encrypted with a device-specific key and synced across iCloud/Google services using E2EE.
The RP still sees a single public key tied to a stable credential ID.
This enables passkey-based authentication from any device owned by the user without manually exporting private keys.
🧑💻 Developer Integration (Backend Flow)
Here’s a Pythonic backend example using fido2
Generate Registration Options:
from fido2.server import Fido2Server
from fido2.rp import RelyingPartyrp = RelyingParty(id="example.com", name="My App")
server = Fido2Server(rp)user = {
"id": b"user-id",
"name": "alice@example.com",
"displayName": "Alice"
}
registration_data, state = server.register_begin(user, user_verification="preferred", resident_key=True)Verify Registration Response:
auth_data = server.register_complete(state, client_data, attestation_object)
store_credential(auth_data.credential_id, auth_data.credential_public_key)Generate Authentication Challenge:
auth_data, state = server.authenticate_begin([credential_id])Verify Assertion Response:
auth_data = server.authenticate_complete(
state,
credentials,
credential_id,
client_data,
authenticator_data,
signature
)⚠️ Security Properties
Origin Binding: Credentials are cryptographically scoped to the domain (
RP ID).Phishing Resistance: Lookalike sites cannot replay the passkeys.
No Shared Secrets: The private key never leaves the device.
Hardware Binding: Optional, for enhanced assurance (e.g., device-local FIDO tokens).
🧩 Final Thoughts
Passkeys represent an important advancement in user authentication, as client-side cryptography takes the place of server-side password storage. For Bitcoiners, cypherpunks, and protocol purists, this is the natural way authentication should’ve worked from day one.
This eliminates the need for password databases.
There will be no need for SMS-based MFA.
All you need is public-key cryptography, which is scoped to specific domains and stored in secure enclaves.
If you’re building auth flows for self-hosted apps, Bitcoin services, or even Chaumian mints, consider using passkeys where applicable. Want to integrate passkeys into a multisig coordinator or a Bitcoin-native identity scheme? Let’s explore those ideas next.
🧵
You can sign up to receive emails each time I publish.
Link to the original Bitcoin White Paper: White Paper:
Dollar-Cost-Average Bitcoin ($10 Free Bitcoin): DCA-SWAN
Access to our high-net-worth Bitcoin investor technical services is available now: cccCloud
“This content is intended solely for informational use. It is not a substitute for professional financial or legal counsel. We cannot guarantee the accuracy of the information, so we recommend consulting a qualified financial advisor before making any substantial financial commitments.





