diff --git a/apps/docs/content/guides/auth/passkeys.mdx b/apps/docs/content/guides/auth/passkeys.mdx
index d97e1ed3f31..0b7c12ae2d5 100644
--- a/apps/docs/content/guides/auth/passkeys.mdx
+++ b/apps/docs/content/guides/auth/passkeys.mdx
@@ -14,7 +14,7 @@ Passkey support is experimental. The API may change without notice. You must exp
-**Requires `@supabase/supabase-js` v2.105.0 and later.** Upgrade your client library to use passkey authentication.
+**Requires `@supabase/supabase-js` v2.105.0 and later, or `supabase_flutter` v2.15.0 and later.** Upgrade your client library to use passkey authentication.
@@ -23,7 +23,7 @@ Passkey support is experimental. The API may change without notice. You must exp
Each sign-in or registration 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()` (registration) or `navigator.credentials.get()` (authentication), prompting the user for biometrics or a security key.
+2. **Ceremony**: the platform's passkey API (`navigator.credentials.create()` / `get()` on web, or a passkey plugin on iOS, Android, and macOS) prompts the user for 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](https://www.w3.org/TR/webauthn-3/#discoverable-credential) for sign-in. The user does not need to provide an email, phone, or username — the authenticator resolves the account from the credential it stores.
@@ -99,6 +99,15 @@ Passkey support is currently experimental and requires explicit opt-in as the AP
+
+
+
```ts
import { createClient } from '@supabase/supabase-js'
@@ -109,11 +118,40 @@ const supabase = createClient(supabaseUrl, supabaseKey, {
})
```
+
+
+
+The Dart SDK does not require an opt-in flag — the methods are annotated `@experimental` so the analyzer surfaces them as preview API. The server independently rejects calls with `passkey_disabled` when the dashboard toggle is off.
+
+```dart
+import 'package:supabase_flutter/supabase_flutter.dart';
+
+await Supabase.initialize(
+ url: supabaseUrl,
+ anonKey: supabaseAnonKey,
+);
+final supabase = Supabase.instance.client;
+```
+
+Platform setup that the library cannot do for you (Associated Domains on iOS/macOS, Digital Asset Links on Android, and including the [`passkeys`](https://pub.dev/packages/passkeys) web SDK in `index.html` on web) is documented in the `supabase_flutter` package README.
+
+
+
+
## Register a passkey
A user must be signed in before they can register a passkey. Typically, you call this from a security settings page, or directly after sign-up.
-`auth.registerPasskey()` runs the full WebAuthn ceremony. It fetches a challenge, invokes the browser API, and verifies the response with Supabase Auth.
+`auth.registerPasskey()` runs the full WebAuthn ceremony. It fetches a challenge, invokes the platform passkey API, and verifies the response with Supabase Auth.
+
+
+
```ts
const { data, error } = await supabase.auth.registerPasskey()
@@ -126,11 +164,30 @@ if (error) {
}
```
-The returned `data` contains the new passkey's metadata:
+
+
+
+```dart
+try {
+ final Passkey passkey = await supabase.auth.registerPasskey();
+ print('Registered passkey ${passkey.id}');
+} on AuthException catch (e) {
+ // The Supabase server rejected the credential.
+ print(e);
+} catch (e) {
+ // User cancelled or the platform ceremony failed.
+ print(e);
+}
+```
+
+
+
+
+The returned passkey contains the new credential's metadata:
```ts
{
- id: string // UUID — use this to update or delete the passkey
+ id: string // UUID — use this to update or delete the passkey
friendly_name?: string // Derived from the authenticator's AAGUID
created_at: string
}
@@ -138,12 +195,21 @@ The returned `data` contains the new passkey's metadata:
A friendly name is automatically derived from the authenticator's Authenticator Attestation GUID (AAGUID). For example, `iCloud Keychain`, `Google Password Manager`, `1Password`. Users can rename their passkey afterwards — see [Manage passkeys](#manage-passkeys).
-See the [`registerPasskey` reference](/docs/reference/javascript/auth-registerpasskey) for the full API.
+See the `registerPasskey` reference ([JavaScript](/docs/reference/javascript/auth-registerpasskey) · [Dart](/docs/reference/dart/auth-registerpasskey)) for the full API.
## Sign in with a passkey
`auth.signInWithPasskey()` runs the full discoverable-credential authentication ceremony. The user picks an account from the authenticator's UI — your app does not need to ask for an email or phone number upfront.
+
+
+
```ts
const { data, error } = await supabase.auth.signInWithPasskey()
@@ -155,12 +221,37 @@ if (error) {
}
```
-See the [`signInWithPasskey` reference](/docs/reference/javascript/auth-signinwithpasskey) for the full API.
+
+
+
+```dart
+try {
+ final AuthResponse res = await supabase.auth.signInWithPasskey();
+ // res.session and res.user are set; the client also fires AuthChangeEvent.signedIn
+ print('Signed in as ${res.user?.email}');
+} on AuthException catch (e) {
+ print(e);
+}
+```
+
+
+
+
+See the `signInWithPasskey` reference ([JavaScript](/docs/reference/javascript/auth-signinwithpasskey) · [Dart](/docs/reference/dart/auth-signinwithpasskey)) for the full API.
## Two-step API
For native flows, custom UI, or full control over the WebAuthn ceremony, use the lower-level `auth.passkey` namespace. Each operation is split into "start" and "verify".
+
+
+
Registration:
```ts
@@ -185,14 +276,57 @@ const { data } = await supabase.auth.passkey.verifyAuthentication({
})
```
+
+
+
+Registration:
+
+```dart
+final registration = await supabase.auth.passkey.startRegistration();
+// Run the platform ceremony yourself (e.g. using a passkey plugin).
+final Map credential = await runRegistrationCeremony(
+ registration.options,
+);
+final passkey = await supabase.auth.passkey.verifyRegistration(
+ challengeId: registration.challengeId,
+ credential: credential,
+);
+```
+
+Authentication:
+
+```dart
+final authentication = await supabase.auth.passkey.startAuthentication();
+// Run the platform ceremony yourself (e.g. using a passkey plugin).
+final Map credential = await runAuthenticationCeremony(
+ authentication.options,
+);
+final AuthResponse res = await supabase.auth.passkey.verifyAuthentication(
+ challengeId: authentication.challengeId,
+ credential: credential,
+);
+```
+
+
+
+
The `options` field returned from `startRegistration` and `startAuthentication` matches the [WebAuthn `PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions) and [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions) shapes (with `ArrayBuffer` fields encoded as base64url).
-See the [`auth.passkey` reference](/docs/reference/javascript/auth-passkey-api) for the full API.
+See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api)) for the full API.
## Manage passkeys
List, rename, and delete the current user's passkeys:
+
+
+
```ts
// List
const { data: passkeys } = await supabase.auth.passkey.list()
@@ -208,14 +342,43 @@ await supabase.auth.passkey.update({
await supabase.auth.passkey.delete({ passkeyId: passkeys[0].id })
```
+
+
+
+```dart
+// List
+final List passkeys = await supabase.auth.passkey.list();
+
+// Rename
+await supabase.auth.passkey.update(
+ passkeyId: passkeys.first.id,
+ friendlyName: 'Work laptop',
+);
+
+// Delete
+await supabase.auth.passkey.delete(passkeyId: passkeys.first.id);
+```
+
+
+
+
`friendlyName` is limited to 120 characters. `last_used_at` is updated each time the passkey is used to sign in.
-See the [`auth.passkey` reference](/docs/reference/javascript/auth-passkey-api) for the full API.
+See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api)) for the full API.
## Admin API
Server-side admin endpoints let you inspect and revoke a user's passkeys. These require the project's secret key and must only be called from a trusted server.
+
+
+
```ts
import { createClient } from '@supabase/supabase-js'
@@ -228,7 +391,26 @@ const { data } = await supabase.auth.admin.passkey.listPasskeys({ userId })
await supabase.auth.admin.passkey.deletePasskey({ userId, passkeyId })
```
-See the [`auth.admin.passkey` reference](/docs/reference/javascript/auth-admin-passkey-api) for the full API.
+
+
+
+```dart
+final supabase = SupabaseClient(supabaseUrl, secretKey);
+
+final List passkeys = await supabase.auth.admin.passkey.listPasskeys(
+ userId: userId,
+);
+
+await supabase.auth.admin.passkey.deletePasskey(
+ userId: userId,
+ passkeyId: passkeyId,
+);
+```
+
+
+
+
+See the `auth.admin.passkey` reference ([JavaScript](/docs/reference/javascript/auth-admin-passkey-api) · [Dart](/docs/reference/dart/auth-admin-passkey-api)) for the full API.
## Error codes
diff --git a/apps/docs/spec/supabase_dart_v2.yml b/apps/docs/spec/supabase_dart_v2.yml
index 2f17b096cad..4371c692617 100644
--- a/apps/docs/spec/supabase_dart_v2.yml
+++ b/apps/docs/spec/supabase_dart_v2.yml
@@ -883,6 +883,48 @@ functions:
providerId: '21648a9d-8d5a-4555-a9d1-d6375dc14e92',
);
```
+ - id: sign-in-with-passkey
+ title: 'signInWithPasskey()'
+ notes: |
+ Signs the user in with a passkey (WebAuthn).
+ - Available on `supabase_flutter` 2.15.0 and later as an extension on `GoTrueClient`.
+ - Drives the full WebAuthn ceremony end to end: starts the challenge with the Supabase server, prompts the user for biometrics or a security key via the platform passkey API, and verifies the credential with the server.
+ - Does not require an existing session. On success the session is persisted and an `AuthChangeEvent.signedIn` event is fired.
+ - For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey-api) namespace instead.
+ - Passkeys are a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
+ - Platform setup the library cannot perform (Associated Domains on iOS/macOS, Digital Asset Links on Android, the `passkeys` web SDK on web) is documented in the `supabase_flutter` package README.
+ params:
+ - name: captchaToken
+ isOptional: true
+ type: String
+ description: Captcha token to be used for captcha verification.
+ examples:
+ - id: sign-in-with-passkey
+ name: Sign in with a passkey
+ isSpotlight: true
+ code: |
+ ```dart
+ final AuthResponse res = await supabase.auth.signInWithPasskey();
+ final Session? session = res.session;
+ final User? user = res.user;
+ ```
+ - id: register-passkey
+ title: 'registerPasskey()'
+ notes: |
+ Registers a new passkey (WebAuthn credential) for the signed in user.
+ - Available on `supabase_flutter` 2.15.0 and later as an extension on `GoTrueClient`.
+ - Drives the full WebAuthn ceremony end to end: starts the registration with the Supabase server, prompts the user to create a credential on the device, and verifies it with the server.
+ - Requires a signed in (non-anonymous) user. If the user has verified MFA factors, the session has to be at `aal2` to manage passkeys.
+ - For native flows or custom UI, use the lower-level [`auth.passkey`](/docs/reference/dart/auth-passkey-api) namespace instead.
+ - Passkeys are a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
+ examples:
+ - id: register-passkey
+ name: Register a passkey for the current user
+ isSpotlight: true
+ code: |
+ ```dart
+ final Passkey passkey = await supabase.auth.registerPasskey();
+ ```
- id: sign-out
title: 'signOut()'
description: |
@@ -2111,6 +2153,162 @@ functions:
]
}
```
+ - id: passkey-api
+ title: 'Auth Passkey'
+ notes: |
+ This section contains methods for WebAuthn passkey registration, authentication, and management. Methods are invoked behind the `supabase.auth.passkey` namespace.
+
+ These methods expose the server side of the WebAuthn ceremony. The client side (the FaceID/TouchID/security key prompt) has to be performed with a platform passkey API: `navigator.credentials.create()`/`get()` on web, or a passkey plugin on iOS/Android/macOS. Options and credentials are exchanged as `Map` in the W3C WebAuthn Level 3 JSON format.
+
+ For a one-call alternative that runs the full ceremony, see [`signInWithPasskey()`](/docs/reference/dart/auth-signinwithpasskey) and [`registerPasskey()`](/docs/reference/dart/auth-registerpasskey) on `supabase_flutter`.
+
+ Passkey support is a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
+ - id: passkey-list
+ title: 'passkey.list()'
+ notes: |
+ Returns the list of passkeys registered to the signed in user.
+ examples:
+ - id: list-passkeys
+ name: List the current user's passkeys
+ isSpotlight: true
+ code: |
+ ```dart
+ final List passkeys = await supabase.auth.passkey.list();
+ ```
+ - id: passkey-update
+ title: 'passkey.update()'
+ notes: |
+ Updates the friendly name of a passkey.
+ params:
+ - name: passkeyId
+ isOptional: false
+ type: String
+ description: ID of the passkey to rename.
+ - name: friendlyName
+ isOptional: false
+ type: String
+ description: New human readable name for the passkey. Limited to 120 characters.
+ examples:
+ - id: rename-passkey
+ name: Rename a passkey
+ isSpotlight: true
+ code: |
+ ```dart
+ final Passkey passkey = await supabase.auth.passkey.update(
+ passkeyId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
+ friendlyName: 'Work laptop',
+ );
+ ```
+ - id: passkey-delete
+ title: 'passkey.delete()'
+ notes: |
+ Deletes a passkey from the signed in user.
+ - If the user has verified MFA factors, the session has to be at `aal2` to manage passkeys.
+ params:
+ - name: passkeyId
+ isOptional: false
+ type: String
+ description: ID of the passkey to delete.
+ examples:
+ - id: delete-passkey
+ name: Delete a passkey
+ isSpotlight: true
+ code: |
+ ```dart
+ await supabase.auth.passkey.delete(
+ passkeyId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
+ );
+ ```
+ - id: passkey-start-registration
+ title: 'passkey.startRegistration()'
+ notes: |
+ Starts the registration of a new passkey for the signed in user.
+ - Requires a signed in (non-anonymous) user.
+ - Pass the returned `options` to the platform's passkey API to create the credential, then call [`passkey.verifyRegistration()`](/docs/reference/dart/auth-passkey-verifyregistration) with the result.
+ examples:
+ - id: start-passkey-registration
+ name: Start a passkey registration
+ isSpotlight: true
+ code: |
+ ```dart
+ final PasskeyRegistrationOptionsResponse registration =
+ await supabase.auth.passkey.startRegistration();
+
+ // Hand registration.options to the platform passkey API.
+ ```
+ - id: passkey-verify-registration
+ title: 'passkey.verifyRegistration()'
+ notes: |
+ Completes the registration of a new passkey and returns the stored [`Passkey`](/docs/reference/dart/auth-passkey-api).
+ params:
+ - name: challengeId
+ isOptional: false
+ type: String
+ description: The challenge ID returned by `passkey.startRegistration()`.
+ - name: credential
+ isOptional: false
+ type: 'Map'
+ description: The credential created by the platform's passkey API, serialized in the W3C `RegistrationResponseJSON` format.
+ examples:
+ - id: verify-passkey-registration
+ name: Verify a passkey registration
+ isSpotlight: true
+ code: |
+ ```dart
+ final Passkey passkey = await supabase.auth.passkey.verifyRegistration(
+ challengeId: registration.challengeId,
+ credential: credential,
+ );
+ ```
+ - id: passkey-start-authentication
+ title: 'passkey.startAuthentication()'
+ notes: |
+ Starts a passkey sign in.
+ - Does not require an existing session.
+ - Pass the returned `options` to the platform's passkey API to obtain an assertion, then call [`passkey.verifyAuthentication()`](/docs/reference/dart/auth-passkey-verifyauthentication) with the result.
+ params:
+ - name: captchaToken
+ isOptional: true
+ type: String
+ description: Captcha token to be used for captcha verification.
+ examples:
+ - id: start-passkey-authentication
+ name: Start a passkey sign in
+ isSpotlight: true
+ code: |
+ ```dart
+ final PasskeyAuthenticationOptionsResponse authentication =
+ await supabase.auth.passkey.startAuthentication();
+
+ // Hand authentication.options to the platform passkey API.
+ ```
+ - id: passkey-verify-authentication
+ title: 'passkey.verifyAuthentication()'
+ notes: |
+ Completes a passkey sign in and returns the new session.
+ - On success the session is persisted and an `AuthChangeEvent.signedIn` event is fired.
+ params:
+ - name: challengeId
+ isOptional: false
+ type: String
+ description: The challenge ID returned by `passkey.startAuthentication()`.
+ - name: credential
+ isOptional: false
+ type: 'Map'
+ description: The assertion produced by the platform's passkey API, serialized in the W3C `AuthenticationResponseJSON` format.
+ examples:
+ - id: verify-passkey-authentication
+ name: Verify a passkey sign in
+ isSpotlight: true
+ code: |
+ ```dart
+ final AuthResponse res = await supabase.auth.passkey.verifyAuthentication(
+ challengeId: authentication.challengeId,
+ credential: credential,
+ );
+ final Session? session = res.session;
+ final User? user = res.user;
+ ```
- id: admin-api
title: 'Overview'
notes: |
@@ -2594,6 +2792,55 @@ functions:
),
);
```
+ - id: admin-passkey-api
+ title: 'Passkey Admin API'
+ notes: |
+ Contains passkey administration methods, accessed under the `supabase.auth.admin.passkey` namespace. Requires a `secret` key.
+
+ Passkey support is a BETA feature and must be enabled for your project in the Supabase Dashboard under Authentication > Configuration > Passkeys.
+ - id: admin-list-passkeys
+ title: 'admin.passkey.listPasskeys()'
+ notes: |
+ Returns the list of passkeys registered to the user with the given ID.
+ params:
+ - name: userId
+ isOptional: false
+ type: String
+ description: User ID whose passkeys should be returned.
+ examples:
+ - id: admin-list-passkeys
+ name: List a user's passkeys
+ isSpotlight: true
+ code: |
+ ```dart
+ final List passkeys = await supabase.auth.admin.passkey.listPasskeys(
+ userId: '11111111-1111-1111-1111-111111111111',
+ );
+ ```
+ - id: admin-delete-passkey
+ title: 'admin.passkey.deletePasskey()'
+ notes: |
+ Deletes a passkey from a user.
+ params:
+ - name: userId
+ isOptional: false
+ type: String
+ description: User ID that owns the passkey.
+ - name: passkeyId
+ isOptional: false
+ type: String
+ description: ID of the passkey to delete.
+ examples:
+ - id: admin-delete-passkey
+ name: Delete a user's passkey
+ isSpotlight: true
+ code: |
+ ```dart
+ await supabase.auth.admin.passkey.deletePasskey(
+ userId: '11111111-1111-1111-1111-111111111111',
+ passkeyId: '34e770dd-9ff9-416c-87fa-43b31d7ef225',
+ );
+ ```
- id: invoke
title: 'invoke()'
description: |