LabHub

Blog

Passkeys and WebAuthn in 2026 — FIDO2, Auth0, Clerk, Stytch, Logto, SuperTokens, Hanko Deep Dive

한국어English日本語

Prologue — In 2026 the death of passwords actually started

When Apple, Google, and Microsoft jointly announced "we will adopt passkeys" back in May 2022, most people shrugged. "Here we go again — OAuth 2.0 took ten years to land" was the typical reaction. As of May 2026, however, the change is visible.

This post maps the passkey and WebAuthn ecosystem as of May 2026. We cover user, developer, and operator perspectives. We start with the standard stack, then platform UX (iOS and Android), then auth SaaS (Auth0, Clerk, Stytch, SuperTokens, Logto, Hanko, Passage), then Korean and Japanese operators, and finally how to gradually migrate from existing password systems.

Code samples deliberately use @simplewebauthn/server and @simplewebauthn/browser as the reference. That library is effectively the Node standard, and other SaaS expose APIs that look the same internally.


1. End of passwords in 2026 — how far have we come

Four years after the 2022 announcement, here are the five most meaningful changes.

First, OS-level first-class citizenship. Through iOS 17 passkeys lived under iCloud Keychain. In iOS 18 the Passwords app split out, sending a clear signal to users that "passkeys are better than passwords." Android 15 did the same with the Credential Manager API.

Second, cross-platform sync. Passkeys had a weakness: "you get locked into one ecosystem." Between 2024 and 2025 1Password, Bitwarden, and Dashlane all added passkey sync, which fixed that problem. A passkey created in iCloud can now be used on Windows through 1Password.

Third, Conditional UI. The most important feature in WebAuthn Level 3. When the user focuses the username field of a login form, passkey candidates show up like autofill. The user does not need to press a separate button. This UX reframes passkeys as "the autofill you already know."

Fourth, Hybrid transport. The desktop-uses-phone-passkey scenario. Show a QR code, scan with the phone, verify proximity over BLE. This was formalized in CTAP 2.2 specification.

Fifth, government and finance adoption. Login.gov started accepting passkeys, and the U.S. SSA is following. Japan kicked off a pilot in 2025 linking My Number Card to passkeys. Korea still has strong incumbents (PASS, financial certificates), but some big tech moved.

Open problems remain.

We return to these problems later in this post.


2. WebAuthn / FIDO2 / CTAP — sorting out the standard stack

The terminology is easy to confuse. One-line summary.

Pictured.

[user browser/app]
   |  WebAuthn JS API (navigator.credentials)
   v
[OS credential provider]  (iOS Passwords / Android Credential Manager / Windows Hello)
   |  CTAP 2.x (USB-HID, NFC, BLE/caBLE)
   v
[authenticator]  (Secure Enclave / TPM / Titan / YubiKey / iPhone / Android phone)

The core of the WebAuthn API is two functions.

// 1) Registration — create a new passkey
const credential = await navigator.credentials.create({
  publicKey: {
    challenge: serverChallenge,            // random nonce from server
    rp: { id: 'example.com', name: 'Example' },
    user: {
      id: new Uint8Array(userIdBytes),     // server-side user.id (UTF-8 bytes)
      name: 'alice@example.com',
      displayName: 'Alice',
    },
    pubKeyCredParams: [
      { type: 'public-key', alg: -7 },     // ES256
      { type: 'public-key', alg: -257 },   // RS256
    ],
    authenticatorSelection: {
      residentKey: 'required',             // = passkey
      userVerification: 'preferred',
    },
    attestation: 'none',                   // most consumer services accept none
  }
})

// 2) Authentication — log in with an existing passkey
const assertion = await navigator.credentials.get({
  publicKey: {
    challenge: serverChallenge,
    rpId: 'example.com',
    userVerification: 'preferred',
    // empty allowCredentials triggers the discoverable-credential picker
    allowCredentials: [],
  },
  mediation: 'conditional', // enables Conditional UI
})

What the server needs to verify.

VerificationMeaning
Challenge matchresponse includes the server-issued challenge
Origin matchclientDataJSON.origin equals the RP origin
rpIdHash matchauthenticatorData.rpIdHash equals SHA-256 of the RP id
flags.UP / UVUser Present / User Verified bits
signCountcounter increases monotonically (clone detection)
Signatureverify clientDataJSON plus authenticatorData with the public key

Implementing this yourself will fail 100 percent of the time. So almost everyone uses a library. We see it next.


3. iOS 18 + macOS Sequoia Passwords app — Apple's move

The quietest yet most meaningful change in iOS 18 (September 2024) was the Passwords app. iCloud Keychain used to be buried in Settings, and breaking it out as a standalone app finally made it look like a password manager.

Highlights.

User flow.

1. Sign up at example.com in Safari
2. While filling the form, "Passwords" offers to save automatically
3. User: one Face ID
4. Passkey registered, stored in iCloud Keychain
5. Other devices (Mac, iPad) log in to the same account immediately

What developers should know.

A note on attestation: there is essentially none. Apple Anonymous Attestation exists, but most consumer services should accept attestation: 'none'. Strong attestation is needed only by enterprise, government, and financial deployments.


4. Android 15 Credential Manager — passkeys and passwords unified

Google built Credential Manager to clean up the fragmentation of Android auth APIs. It went beta in Android 14 and became GA in Android 15. The core idea is "passwords, passkeys, and federated logins (Google Sign-In) behind one API."

// Android Credential Manager — registration
val request = CreatePublicKeyCredentialRequest(
  requestJson = serverChallengeJson,
  preferImmediatelyAvailableCredentials = true,
)
val credential = CredentialManager.create(context)
  .createCredential(activity, request)

// Authentication
val getRequest = GetCredentialRequest(
  credentialOptions = listOf(
    GetPublicKeyCredentialOption(serverRequestJson),
    GetPasswordOption(),  // existing passwords are exposed too
  ),
)
val response = CredentialManager.create(context)
  .getCredential(activity, getRequest)

Wins.

Differences from the WebAuthn standard.


5. 1Password / Bitwarden / Dashlane — cross-platform passkey sync

OS-side sync (Apple Keychain, Google Password Manager) only works within one ecosystem. So the role of third-party password managers grew. Between 2024 and 2025 all three major ones added passkey sync.

1Password.

Bitwarden.

Dashlane.

What they share is a precise implementation of WebAuthn Conditional UI. Browser extensions do not fight the OS credential provider; they let the user pick.


6. WebAuthn Level 3 (2024.9 W3C Recommendation) — Conditional UI and Auto-fill

WebAuthn Level 2 became a W3C Recommendation in 2021, and Level 3 reached the final recommendation in September 2024. Five key changes.

1. Conditional UI (Auto-fill).

// Call early at page load. mediation: 'conditional' is the key.
const cred = await navigator.credentials.get({
  publicKey: {
    challenge: serverChallenge,
    rpId: 'example.com',
    allowCredentials: [],          // must be empty for discoverable lookup
  },
  mediation: 'conditional',
  signal: abortController.signal,
})

2. JSON encoding. PublicKeyCredential.toJSON() is standardized. The server receives JSON instead of handling ArrayBuffers directly.

3. PRF extension. Pseudo-Random Function extension. Lets you derive a deterministic secret (for example an E2EE key) from a passkey. 1Password demoed a PoC of unlocking vaults this way.

4. largeBlob stabilization. Store small data (for example a user-defined label) alongside the authenticator.

5. Direct attestation formats. Apple and Google brought additional attestation formats into the standard.


7. Hybrid transport (caBLE then CTAP 2.2) — phone-as-authenticator

"I am logging into a website on my laptop but the passkey is on my phone" is the most common pain point. From 2022 it was experimented with under the name caBLE (Cloud Assisted Bluetooth Low Energy), and in 2024 it landed in CTAP 2.2 under the official name Hybrid transport.

Flow.

1. Browser on the PC shows a QR code
2. User scans with the phone camera
3. Phone performs a handshake through the cloud (Apple/Google push infra)
4. Phone and PC confirm proximity via BLE signaling
5. User does Face ID/Touch ID/fingerprint on the phone
6. Phone produces a signature and relays it via the cloud to the PC
7. PC forwards the signature to the RP server — login succeeds

The security model of this flow is interesting.

Why this is good.

Downsides.


8. Auth0 / Okta — the enterprise camp

When large companies adopt passkeys, Auth0/Okta is the first thing that comes to mind. Both formally support passkeys.

Auth0 (Okta subsidiary).

Okta Workforce Identity Cloud.

Microsoft Entra ID (formerly Azure AD).

What this camp shares is governance. Passkey policies are managed by IT, not by the user.

Consumer SaaS rarely addresses these.


9. Clerk / Stytch / Passage — developer-friendly SaaS

In the startup and developer camp a different class of SaaS is popular.

Clerk.

// Clerk — Next.js example (concept)
import { SignIn } from '@clerk/nextjs'

export default function Page() {
  return <SignIn signInUrl="/sign-in" />
  // passkeys, email, and OAuth are exposed automatically
}

Stytch.

Passage (acquired by 1Password).

What the three have in common.


10. Logto / SuperTokens / Hanko — open source options

Teams that need self-hosting, that face GDPR or data sovereignty issues, or that want to control cost have open source options.

SuperTokens.

// SuperTokens — passkey recipe (concept)
import Passkey from 'supertokens-node/recipe/passkey'

SuperTokens.init({
  recipeList: [
    Passkey.init({
      // RP info, challenge generation, and verification handled by the library
    }),
    Session.init(),
  ],
})

Logto.

Hanko.

<!-- Hanko — drop-in component (concept) -->
<hanko-auth></hanko-auth>
<script type="module">
  import { register } from '@teamhanko/hanko-elements'
  register({ shadow: true })
</script>

Shared strengths of the open source camp.

Weaknesses.


11. Korea — Naver / Kakao / Toss passkey adoption

Korea is slower than the global average at adopting passkeys. Two reasons. First, the Financial Certificate / Joint Certificate / PASS ecosystem is strong. Second, finance still relies heavily on ARS and OTP.

Naver.

Kakao.

Toss.

Public sector and finance.

Implications.


12. Japan — docomo / au / SoftBank ID, Yahoo!Japan

Japan is a bit more standardized in auth than Korea. Telco IDs, My Number, and the LINE/Yahoo! strategy interlock.

docomo ID.

au ID (KDDI).

SoftBank ID / Y!mobile.

Yahoo!Japan.

LINE.

My Number Card.

The Japanese characteristic is that telcos and platforms are aggressive about passkey adoption. The government is also pulling compatibility together through My Number Card.


13. Passkey migration strategy — how to adopt gradually

How do you slip passkeys into an existing password system. Five steps.

Step 1: add passkey registration as an option.

Step 2: add Conditional UI to the login form.

Step 3: make passkeys the default at sign-up.

Step 4: upsell existing password users.

Step 5: introduce passkey-only mode.

Account recovery — the most important part.

Metrics.

Track these four and gradually shrink the password share.


14. References

Standards / specs.

OS / platforms.

Libraries.

SaaS / OSS auth.

Password manager passkey sync.

Korea / Japan case studies.

Government / compliance.

Comments

No comments yet.

Sign in to leave a comment