Passkeys for Sysadmins: What Actually Happens on the Wire, Hands-On

A sysadmin-grade walkthrough of what actually happens on the wire with passkeys and WebAuthn: the registration and authentication ceremonies, discoverable credentials, what sync really costs you, hardware keys, phishing resistance and its limits, recovery planning, FIDO2 SSH keys, and the honest list of where passkeys do not apply.

Passkeys for Sysadmins: What Actually Happens on the Wire, Hands-On

If you have been following the authentication conversation on this blog, you already know the verdicts: SMS two-factor authentication is a weak second factor at best, and mandatory password changes made security worse, not better.

Both posts ended at the same door: the future of logins is a passkey, a WebAuthn credential bound to a device, phishing-resistant by construction. What neither post did was open that door and show what actually happens on the wire.

This post is the sysadmin version of that walkthrough. I cover the WebAuthn challenge and response ceremonies step by step, what makes a credential discoverable and why that is the definition of a passkey, how sync moves credentials between devices and what it costs you, why the hardware key is still the sysadmin default, the recovery plan most services forget to ship, how SSH uses FIDO2 without being WebAuthn, and the honest list of places where passkeys do not apply. The reasoning lives in the SMS post and the password post; this one is the protocol.

The short version

  • A passkey is a WebAuthn credential, not a password replacement. The authenticator generates a key pair, the private key never leaves it, and the server stores only the public key. A server breach leaks public keys, which are useless without the device.
  • Registration and login are both challenge-response ceremonies. The server sends a random challenge, the authenticator signs it after user verification, and the server checks the signature, the challenge, the origin, and the RP ID before it trusts anything.
  • The RP ID is the anti-phishing mechanism. The signature is bound to your exact domain. A fake clone of your login page produces a signature bound to its own domain, which your server rejects.
  • Discoverable credentials are passkeys; everything else is a prompt for a credential ID. A resident key stores the credential and the user on the authenticator, so login works without typing a username. That is the property that makes passkeys feel like passwords.
  • Sync moves the credential, and the trust moves with it. Synced passkeys travel through Apple, Google, or a password manager fabric, end to end encrypted, but the provider account becomes part of your authentication.
  • SSH uses FIDO2 keys, not WebAuthn passkeys. ssh-keygen -t ed25519-sk puts the private key on a security key and leaves only a handle on disk. Same hardware, different protocol, no website in the loop.
  • Recovery is the gap you have to design around. There is no server-side reset for a passkey. Registration codes, multiple enrolled authenticators, and a tested restore path are the plan.
Verified
  • WebAuthn (W3C)Level 3, proposed Recommendation 2026-07-20
  • OpenSSH FIDO2 supportsk-ecdsa-sha2-nistp256 and sk-ssh-ed25519 since OpenSSH 8.2
  • YubiKey firmware5.2.3+ for ed25519-sk resident keys
  • NIST SP 800-63BRev 4 (2025) with passkey supplement

Checked 2026-08-29 against w3.org/TR/webauthn-3, the Yubico FIDO2 SSH guide, the FIDO Alliance passkey FAQ, MDN WebAuthn docs, and the NIST 800-63B Rev 4 pages. Standards and firmware move fast; re-check before you rely on exact versions.

What WebAuthn actually is

WebAuthn, the Web Authentication API, is the W3C standard that lets a website, called the relying party, register and authenticate users with asymmetric cryptography instead of shared secrets. The user's device or security key is the authenticator. On registration it generates a public and private key pair; the private key stays on the authenticator, and the public key plus a credential ID is stored by the server. On login the authenticator signs a challenge, and the server verifies the signature with the stored public key.

The name passkey is the product word for a WebAuthn credential that is discoverable, meaning the authenticator can present it without the server first naming it. The FIDO Alliance defines a passkey as an authentication credential based on FIDO standards that can be stored on a phone, a computer, or a hardware security key. WebAuthn Level 3, which adds the passkey concept to the spec, was proposed as a W3C Recommendation on 20 July 2026.

Contrast that with what it replaces. SMS codes and TOTP are shared secrets: the server knows the same value the user enters, so a phished code or a server breach defeats them. With WebAuthn the server holds only a public key. There is nothing to steal from the server that helps an attacker sign in.

The registration ceremony, step by step

  1. The server creates a fresh random challenge and a set of options: the RP ID (your domain), the user ID and display name, the desired algorithms, and policy flags such as userVerification and residentKey.
  2. The browser passes those options to the authenticator through navigator.credentials.create(). The authenticator asks for user verification: a fingerprint, a face, or a PIN.
  3. The authenticator generates a key pair, stores the private key (optionally as a discoverable credential), and returns a public key, a credential ID, and a signature over the challenge and the RP ID.
  4. The server verifies the signature and the origin, stores the public key and credential ID against the user, and the ceremony is done. From now on the user is identified by possession of that authenticator.
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: serverChallenge, // random bytes from your server
    rp: { id: "example.com", name: "Example" },
    user: { id: userIdBytes, name: "admin", displayName: "Admin" },
    pubKeyCredParams: [{ type: "public-key", alg: -7 }],
    authenticatorSelection: {
      userVerification: "required",
      residentKey: "required"
    }
  }
});

The authentication ceremony: what crosses the wire

When the user returns, the server generates a second fresh challenge and sends it with the RP ID. The browser calls navigator.credentials.get(). The authenticator demands user verification again, then signs the data. What comes back is the assertion: authenticator data (the RP ID hash, the user-present and user-verified flags, and a signature counter), the client data JSON (the challenge and the origin), and the signature over both.

The server then runs the checklist that is the whole point of the ceremony:

  • The signature verifies against the stored public key.
  • The challenge in the assertion matches the challenge the server sent, which is what kills replay attacks.
  • The origin in the client data matches the expected site, and the RP ID hash matches the RP ID, which is what kills phishing.
  • The user-verified flag is set if your policy requires it.
  • The signature counter has increased, which is the defense against cloned authenticators.
const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: serverChallenge,
    rpId: "example.com",
    userVerification: "required"
  }
});

Discoverable credentials: why passkeys mean resident

A WebAuthn credential can be non-discoverable or discoverable. With a non-discoverable credential, the server must tell the browser which credential ID to use by listing it in allowCredentials. The authenticator holds the private key but not the mapping to your account. With a discoverable credential, also called a resident key, the authenticator stores the credential together with the user handle. The server can send an empty allow list, and the authenticator presents the matching credential itself, which is why the browser can show you a list of accounts without you typing a username.

That small difference is the entire passkey experience. A non-discoverable credential is still phishing-resistant, but it needs the server to identify the user first, which usually means a username field. A discoverable credential is the thing the industry calls a passkey: the device is the identifier, and login starts with the authenticator, not with typing.

One hardware note for the future sysadmin: resident keys consume storage on the authenticator. A small security key has room for a limited number of them, so treat resident slots as a resource when you plan enrollment, and use non-resident credentials for accounts where the server can identify the user anyway.

Sync: the credential leaves the device, and trust moves with it

Consumer passkeys are usually synced passkeys. The authenticator material is copied into a cloud-backed fabric, iCloud Keychain, Google Password Manager, 1Password, Bitwarden, and synchronized across the user's devices, end to end encrypted. From the user's perspective that is the killer feature: lose the phone, and the passkey is still on the laptop. From the sysadmin's perspective it is a new trust dependency.

The FIDO Alliance enterprise guidance is explicit about the trade: synced passkeys depend on the provider's synchronization fabric and its security controls. You are adding a third party to the authentication decision, and the account that controls that fabric, the Apple ID, the Google account, the password manager account, becomes a target. If an attacker takes over the sync account, they may be able to recover the passkeys in it.

Hardware keys: the sysadmin default

A FIDO2 security key, the YubiKey and its competitors, is a roaming authenticator that speaks CTAP2 over USB, NFC, or Bluetooth. The private key material is generated inside the key and never leaves it; the key cannot be copied, backed up, or synchronized. Signing requires the key plus a touch, and with a PIN or a biometric, user verification.

For infrastructure accounts this is the right default. The credential cannot be phished, cannot be exfiltrated by malware that reads your disk, and does not depend on a cloud account. The cost is operational: you must carry the key, and you must plan for loss, which is why the standard answer is two keys enrolled on every account that matters, with one stored somewhere safe.

Synced passkey vs hardware security key
FeatureSynced passkeyHardware security key
Private key livesPlatform secure enclave, copied through the sync fabricOn the key, never exported
Sync across devicesYes, via the provider fabricNo; re-enroll or use resident recovery
Phishing resistance1Yes, RP ID bindingYes, RP ID binding
Recovery on device lossProvider account recoverySpare key or registration codes
Trust dependencyThe sync provider's accountPhysical possession of the key
Best forConsumer accounts, daily logins, convenienceAdmin accounts, break-glass, SSH, high assurance
  • Private key lives

    Synced passkey
    Platform secure enclave, copied through the sync fabric
    Hardware security key
    On the key, never exported
  • Sync across devices

    Synced passkey
    Yes, via the provider fabric
    Hardware security key
    No; re-enroll or use resident recovery
  • Phishing resistance1

    Synced passkey
    Yes, RP ID binding
    Hardware security key
    Yes, RP ID binding
  • Recovery on device loss

    Synced passkey
    Provider account recovery
    Hardware security key
    Spare key or registration codes
  • Trust dependency

    Synced passkey
    The sync provider's account
    Hardware security key
    Physical possession of the key
  • Best for

    Synced passkey
    Consumer accounts, daily logins, convenience
    Hardware security key
    Admin accounts, break-glass, SSH, high assurance
  1. both are FIDO2

Phishing resistance: what it stops and what it does not

What it stops is the attack class that has defined phishing for two decades: a fake login page that harvests a password and a session. A passkey never sends a secret across the wire, the signature is bound to the RP ID, and the challenge prevents replay. A server breach gives an attacker public keys, not credentials. Credential stuffing is dead on accounts that actually require the passkey.

Here is what it does not stop, and the list matters more:

  • Recovery flow phishing. Attackers have moved from login screens to account recovery. If your recovery path is email or backup codes, that path is still phishable. Secure the recovery path with the same standard as the login.
  • The password fallback. Most services keep password login enabled alongside passkeys. The account remains phishable through the path that still accepts a password, until you remove or lock it down.
  • Malware on the enrolled device. A passkey signs what the browser asks it to sign. If the device is compromised, the attacker can ride an authenticated session or prompt for the same gestures the user would give.
  • Sync account takeover. A synced passkey is only as strong as the provider account that controls it.

Recovery: plan it before you remove the password

There is no Forgot passkey button that works. The server cannot reset a credential it does not hold; losing the authenticator without a plan means losing the account. The discipline is the same one this blog applies to encryption keys in the backup post: recovery material is part of the backup, not an afterthought.

  • Enroll more than one authenticator. Two hardware keys, or a key plus a device-bound passkey, so one loss is not an outage.
  • Store registration and recovery codes offline. Print them, put them in a sealed envelope or a vault, and treat them with the same care as a master password.
  • Test the restore path. Recover a test account from the codes and the spare key before you need to do it for real.
  • Document the process. The day the admin's laptop dies is not the day to discover the recovery codes were never generated.

SSH and FIDO2: keys, not passkeys, hands-on

Here is where the sysadmin actually lives. SSH does not use WebAuthn passkeys; it uses the same FIDO2 hardware through a separate path that OpenSSH implemented in version 8.2. The key types are sk-ecdsa-sha2-nistp256@openssh.com and sk-ssh-ed25519@openssh.com. There is no relying party and no website-issued challenge: the SSH client asks the security key to sign the session data, and the server verifies the signature against the public key in authorized_keys.

The important difference from a file-based key: the private key never touches the disk. The file ssh-keygen writes is a public key plus a handle that references the credential on the key. Copying the file to another machine does not move the private key; only the hardware can sign.

generate a resident SSH key with verify-required
ssh-keygen -t ed25519-sk -O resident -O verify-required -C you@example.com
Generating public/private ed25519-sk key pair.
You may need to touch your authenticator to authorize key generation.
Enter PIN for authenticator:
Your identification has been saved in /home/you/.ssh/id_ed25519_sk
Your public key has been saved in /home/you/.ssh/id_ed25519_sk.pub

Because the credential is resident, you can recover it on a new machine without copying anything private:

recover a resident key on a new machine
ssh-keygen -K
Enter PIN for authenticator:
Saved ed25519-sk key to id_ed25519_sk
Saved ed25519-sk key to id_ed25519_sk.pub

# then load it into the agent
ssh-add ~/.ssh/id_ed25519_sk
Confirm user presence for key ed25519-sk SHA256:...
WebAuthn passkey vs SSH FIDO2 key
FeatureWebAuthn passkeySSH FIDO2 key
StandardWebAuthn (W3C), browser APIOpenSSH FIDO2, no browser
Server sideRelying party stores public key and credential IDauthorized_keys stores the public key
ChallengeServer-issued random challenge in the browserSSH server verifies signature over session data
Private key locationPlatform authenticator or security keySecurity key only
DiscoverableYes, that is what makes it a passkeyOptional via -O resident
Use forWeb logins and appsSSH, git, console access
  • Standard

    WebAuthn passkey
    WebAuthn (W3C), browser API
    SSH FIDO2 key
    OpenSSH FIDO2, no browser
  • Server side

    WebAuthn passkey
    Relying party stores public key and credential ID
    SSH FIDO2 key
    authorized_keys stores the public key
  • Challenge

    WebAuthn passkey
    Server-issued random challenge in the browser
    SSH FIDO2 key
    SSH server verifies signature over session data
  • Private key location

    WebAuthn passkey
    Platform authenticator or security key
    SSH FIDO2 key
    Security key only
  • Discoverable

    WebAuthn passkey
    Yes, that is what makes it a passkey
    SSH FIDO2 key
    Optional via -O resident
  • Use for

    WebAuthn passkey
    Web logins and apps
    SSH FIDO2 key
    SSH, git, console access

Where passkeys do not apply

  • Server-to-server and CI. There is no user to present a gesture. Machine identities stay with SSH keys, workload identity, and short-lived tokens.
  • Shared and kiosk devices. Passkeys assume a one-to-one user-to-device binding. A shared terminal or a kiosk breaks the model, because the authenticator is tied to one user.
  • Legacy protocols and clients. Anything that predates WebAuthn: LDAP, IMAP and SMTP, some VPNs, RDP without a modern client, databases, scripts, will keep needing passwords, TOTP, or hardware-backed tokens.
  • Non-interactive automation. Any flow that cannot present an authenticator and take a gesture.
  • Environments where sync is forbidden. Air-gapped networks and high-assurance environments often require device-bound credentials, which rules out synced passkeys and may rule out platform authenticators that back up.
  • The migration bootstrap. You need a password or a recovery path to enroll the first passkey. Plan the transition so the fallback is removed after migration, not kept forever.

Which should you pick?

  • Choose device-bound hardware keys when: you are the person who administers the account, the account is admin or break-glass, you run the infrastructure yourself, offline resilience matters, or the organization forbids cloud sync. The security key is the sysadmin default for a reason.
  • Choose synced passkeys when: the goal is mass adoption, users live on consumer platforms, and convenience and recovery matter more than strict device binding. That is most of your user base.
  • Choose both when: you want the convenience for daily accounts and hardware binding for the accounts that matter: synced passkeys for the team, security keys for root and recovery.
  • Consider neither when: the flow is machine-to-machine or non-interactive. SSH keys and workload identity are the honest answer, and nothing in the passkey world replaces them.

My own rule after running this for a while: passkeys are the right answer for interactive human authentication, and hardware keys are the right answer for the accounts that would hurt. The wire protocol is genuinely good, the phishing resistance is real, and the gaps, sync trust, recovery, password fallback, are all things you can engineer around once you know they exist. The SMS post ended with move to passkeys where the service supports them. This post is the part where you know what you are moving to.

Official sources

  • WebAuthn Level 3 (W3C): https://www.w3.org/TR/webauthn-3/
  • MDN Web Authentication API: https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API
  • FIDO Alliance passkey FAQ: https://fidoalliance.org/passkeys/
  • Yubico FIDO2 SSH guide: https://developers.yubico.com/SSH/Securing_SSH_with_FIDO2.html
  • NIST SP 800-63B: https://pages.nist.gov/800-63-4/sp800-63b.html
  • Our SMS 2FA post: https://systhoughts.com/posts/sms-based-two-factor-authentication-is-no-longer-effective
  • Our password change post: https://systhoughts.com/posts/mandatory-password-changes-are-no-longer-effective
  • Our backup and recovery keys post: https://systhoughts.com/posts/3-2-1-backup-rule-not-enough-self-hosted-infrastructure

Are you rolling out passkeys yet? Where did you draw the line between synced passkeys and hardware keys, and how are you handling SSH and recovery? Drop it in the comments.

Until next time, keep your systems thoughtful.

No comments yet