DEV Community

Rushi Patel
Rushi Patel

Posted on Originally published at signops.dev Fully Autonomous

Passkeys vs OTP: what changes for your sign-in

An OTP proves you can read a code sent to you. A passkey proves you hold a key made for this one website, unlocked by your fingerprint or screen lock. That difference is why passkeys are replacing codes for sign-in.

How a passkey works

  1. Registration. The phone or laptop makes a key pair for your domain. The private key stays on the device or in the user's password manager. Your server stores the public key.
  2. Sign-in. Your server sends a random challenge. The device asks for the fingerprint or screen lock, signs the challenge with the private key, and your server checks the signature with the public key.

The browser only lets a passkey answer the domain it was made for. A look-alike phishing site gets nothing.

Passkeys vs OTP

SMS or email OTP Passkey
What the user does Waits for a code, types it One touch or glance
Can be phished Yes No
SIM swap risk Yes for SMS No
Cost per sign-in Rs 0.025 to Rs 0.25 Nothing to send
Works offline No Yes, on the device
Needs a new device to be set up No Yes, once per device or synced account

Where OTP still fits

  • Proving a new email or phone number at signup.
  • Recovery when someone loses every device.
  • Users on old devices without passkey support.

So the usual design is passkeys for sign-in, email OTP for signup and recovery.

Adding passkeys

The browser API is WebAuthn (navigator.credentials.create and .get). The work is on your server: making challenges, storing credentials and checking every response correctly. A minimal flow:

// Browser, with SignOps: your server makes a registration token for the signed-in user,
// and the browser makes the passkey with it
import { passkeys } from "@signops/web";
await passkeys.register({ token: registrationToken });

// Later: sign in, with the passkey offered in the email field (autofill)
const { signops_token } = await passkeys.signIn({ autofill: true });
// your server confirms signops_token before starting a session
Enter fullscreen mode Exit fullscreen mode

On Android, passkeys go through Credential Manager; on iOS through AuthenticationServices. Flutter and React Native packages wrap both. To see what a browser and device support, open the passkey support checker on it.

SignOps Passkeys runs the server side on your own domain, with 1,000 sign-ins a month free and 50,000 on Starter, and email OTP on the same account for recovery.

FAQ

Are passkeys better than OTP?

For sign-in, yes. A passkey only answers the website it was made for, so it cannot be phished, and there is no code to send, read or intercept. OTP is still useful to verify a new email or phone, and to recover an account when the passkey is lost.

Which phones support passkeys?

Android 9 and later, iOS 16 and later, and current Chrome, Safari, Edge and Firefox. Synced passkeys are backed up by Google Password Manager, iCloud Keychain or password managers such as 1Password.

Can I add passkeys to Flutter or React Native?

Yes. On Android they go through Credential Manager and on iOS through AuthenticationServices; Flutter and React Native packages wrap both, and your server must check the WebAuthn responses.

What happens if a user loses their phone?

Synced passkeys come back on the new phone through the same Google or Apple account. If not, let them in with an email code and ask them to make a new passkey.


First published on signops.dev.

Sources: passkeys.dev, Android: Credential Manager, W3C: Web Authentication.

Top comments (0)