Olympus Docs
CookbookAuth flows

Cross-device passkey login

Phone unlocks desktop, the WebAuthn way

A user has a passkey on their phone. They want to log in on a desktop browser that doesn't have a passkey of its own. WebAuthn's "hybrid" transport (also called CTAP2 / caBLE) makes this work.

How it looks

  1. User clicks "Sign in with passkey" on desktop.
  2. Browser shows QR code.
  3. User scans with phone camera.
  4. Phone prompts biometric.
  5. Approval signed by phone, transmitted to desktop via Bluetooth proximity check.
  6. Desktop session created.

No passkey synced to desktop. No password.

Browser/OS requirements

  • Phone: iOS 16+, Android 9+ (most modern devices).
  • Desktop: Chrome 108+, Safari 16+, Edge 108+, Firefox 119+.
  • Both devices: Bluetooth on. They don't need to be on the same network.

In Hera / Kratos

Kratos's WebAuthn flow already supports hybrid. No special config, the browser handles transport negotiation. From Olympus's side, you just enable WebAuthn:

# kratos.yml
selfservice:
  methods:
    webauthn:
      enabled: true
      config:
        rp:
          display_name: Your App
          id: your-domain.com
        passwordless: true

passwordless: true lets WebAuthn complete a login flow standalone (no password first).

User flow

When the user clicks "Sign in with passkey":

<button id="passkey-login">Sign in with passkey</button>
<script>
  document.getElementById("passkey-login").addEventListener("click", async () => {
    const options = await fetch("/self-service/login/api?aal=aal1&method=passkey", {
      credentials: "include"
    }).then(r => r.json());

    const cred = await navigator.credentials.get({
      publicKey: parsePublicKeyOptions(options),
      mediation: "optional",
    });

    // Browser handles QR / Bluetooth dance internally.
    // Submit assertion to Kratos.
    await fetch(options.ui.action, {
      method: "POST",
      credentials: "include",
      body: JSON.stringify({
        method: "passkey",
        passkey_login: serializeCredential(cred),
      }),
    });
  });
</script>

The browser, not your code, displays the QR code and orchestrates the cross-device step. You just call navigator.credentials.get with mediation: "optional" to allow the conditional UI.

Roaming authenticator vs platform

  • Roaming: USB security key (YubiKey), or phone via hybrid. Works across devices.
  • Platform: tied to the device (Touch ID/Face ID on Mac, Windows Hello on PC).

authenticatorSelection.authenticatorAttachment controls preference:

authenticatorSelection: {
  authenticatorAttachment: "cross-platform", // wants roaming / hybrid
  // or "platform", limits to device's own biometrics
  // or omit, let browser choose
}

For "sign in on a new desktop with my phone," you want cross-platform OR no preference.

Enrolling cross-device

A user can enroll a passkey from their phone while they're signing in on desktop:

  1. Desktop login → password auth.
  2. Settings → "Add passkey."
  3. Choose "Use a phone or tablet" in the OS dialog.
  4. QR on desktop → scan with phone.
  5. Phone biometric → passkey created on phone, attached to user.
  6. Next time: desktop login → "Sign in with passkey" → same QR/Bluetooth dance.

Troubleshooting

QR scans but Bluetooth fails.

  • Both devices need Bluetooth on.
  • iCloud Keychain / Google Password Manager sync state matters.
  • Older devices may not support the proximity check.

No QR appears, only "use security key."

  • Browser is older.
  • OR authenticatorSelection.authenticatorAttachment: "platform" was set, excluding hybrid.

"Could not verify your identity" after biometric.

  • Origin mismatch: rp.id in Kratos config doesn't match the domain the user is on.
  • Set rp.id: your-domain.com (no subdomain) for it to work across subdomains.

UX considerations

  • Add a fallback: "Sign in another way" link below the passkey button. Goes to password.
  • First-time users won't know what "passkey" is. Use language like "Sign in with phone biometric" or "Sign in without password."
  • Cross-device adds 5-10s vs same-device. Worth it for new-desktop scenarios.

On this page