Passkeys for Supabase Auth (Beta)

May 28, 2026

We're excited to announce the beta release of Passkeys for Supabase Auth — a passwordless, phishing-resistant credential built on the WebAuthn standard.

With passkeys, users sign in with biometrics (Face ID, Touch ID, Windows Hello), a device PIN, or a hardware security key. Supabase Auth stores the public key needed for verification; private key material remains managed by the user’s authenticator or credential provider.

How does it work?#

Each passkey enrollment or sign-in is a WebAuthn ceremony with three steps:

  1. Options: the client requests a challenge from Supabase Auth.
  2. Ceremony: the browser invokes navigator.credentials.create() (register) or navigator.credentials.get() (sign in), prompting the user to approve with biometrics or a security key.
  3. Verify: the signed response is sent back to Supabase Auth, which validates the challenge and either stores the new credential or issues a session.

Supabase Auth uses discoverable credentials, so users don't need to type an email or username — the authenticator resolves the account from the credential it already stores.

Enable passkeys in the Dashboard#

Open Authentication → Passkeys in the Dashboard, toggle on Enable Passkey authentication, and fill in your WebAuthn relying party details:

  • Relying Party Display Name: human-readable name shown during the passkey prompt (e.g. "My App").
  • Relying Party ID: your bare domain (e.g. example.com). No scheme, port, or path.
  • Relying Party Origins: up to 5 allowed origins (e.g. https://example.com,https://app.example.com).

The Dashboard pre-fills these from your project's Site URL and project name.

Passkeys can also be configured via the CLI and the Management API.

Use it from your app#

[!NOTE] The Passkeys API is currently experimental and requires an explicit opt-in as the API may change without notice during the beta phase.

Opt in to the experimental API when creating the client:


_10
import { createClient } from '@supabase/supabase-js'
_10
_10
const supabase = createClient(supabaseUrl, supabasePublishableKey, {
_10
auth: {
_10
experimental: { passkey: true },
_10
},
_10
})

Register a passkey for an authenticated user — typically from a security settings page or right after sign-up:


_10
const { data, error } = await supabase.auth.registerPasskey()
_10
// data: { id, friendly_name, created_at }

Sign in with a passkey — no email or phone needed upfront; the authenticator picks the account:


_10
const { data, error } = await supabase.auth.signInWithPasskey()
_10
// data.session and data.user are set; a SIGNED_IN event is dispatched

Manage passkeys — list, rename, and delete from the current user's account:


_10
const { data: passkeys } = await supabase.auth.passkey.list()
_10
_10
await supabase.auth.passkey.update({
_10
passkeyId: passkeys[0].id,
_10
friendlyName: 'Work laptop',
_10
})
_10
_10
await supabase.auth.passkey.delete({ passkeyId: passkeys[0].id })

What we'd like to know from you#

  • Any bugs or rough edges you hit during passkey registration or sign-in flows.
  • Friction when configuring the relying-party settings in the Dashboard, CLI, or Management API.
  • Feedback on integrating passkeys in native or mobile flows.
  • Suggestions for improving the API ergonomics or documentation.

Drop your feedback in this thread or open an issue.

Build in a weekend, scale to millions