pealdevDOCS
Back to peal.dev

AUTH STARTER · PASSKEY (WEBAUTHN)

Passkey (WebAuthn)

Face ID, Touch ID, hardware keys. Once a user adds one, they never type a password again on that device.

Passkeys are WebAuthn under the hood. The user registers a credential (their device's biometric or a YubiKey), and from then on "sign in with passkey" is a single Face ID prompt. No password to type, no email to wait for.

Turn it on

TypeScript
// src/auth.config.ts
plugins: {
  passkey: { enabled: true },
},

The server-side rpID and origin come from authServerConfig.passkey — derived from BETTER_AUTH_URL. You don't usually touch them.

Register a passkey (after sign-in)

TypeScript
await authClient.passkey.addPasskey();
// Browser prompts the user — Face ID, Touch ID, security key, etc.

Sign in with passkey

TypeScript
await authClient.signIn.passkey();

rpID must match the hostname exactly

rpID = new URL(BETTER_AUTH_URL).hostname. If your prod URL is https://app.yourdomain.com, rpID is app.yourdomain.com. Passkeys registered against one rpID are USELESS at another — including subdomains. Choose your canonical domain BEFORE launch.

Passkeys do NOT work on http (except localhost)

WebAuthn requires HTTPS. The browser will silently fail to register a passkey on an http:// staging URL. localhost is the only HTTP exception. Use HTTPS in any test environment users will touch.

Account recovery is on you

If a user loses all their devices, their passkey is gone. Always offer at least one fallback method (email OTP, password) so users aren't locked out forever. The dashboard component passkey-manage.tsx lets them register multiple passkeys — encourage that.

The DB table

Enabling passkey adds a passkey table to the schema (credential id, public key, counter, transports). Run db:push (dev) or generate + migrate (prod) after toggling on.