- Primary-factor sign-in: the user signs in with only a passkey, with no OTP and no password. Driven by
loginWithPasskey. Requireslogin_enabledin the passkey configuration. - Step-up factor: the user already has a session and proves a passkey to acquire a sensitive scope. Driven by
continueStepUpWithPasskey.
Requirements
Passkeys need a more recent OS version than the rest of the SDK, and the SDK version that added them:
On a device that cannot run a passkey ceremony, the passkey methods fail with a “passkey not supported” error (see Errors). Offer another sign-in method in that case.
Platform setup
Before any passkey ceremony, the operating system verifies that your app is associated with the relying-party host (therp_id of your passkey configuration). The association has two sides, and both are required: the server authorizes the app, and the app declares the host.
Authorize your apps in the passkey configuration
Add your native apps to the passkey configuration with the Management API. Prelude then serves the association documents (/.well-known/apple-app-site-association and /.well-known/assetlinks.json) on your app’s Prelude domain: <APP_ID>.session.prelude.dev, or your custom domain.
The operating system fetches these documents from the rp_id host. When your rp_id is your app’s Prelude domain, nothing more is needed. When it is another host, serve the same two documents on that host yourself.
Only
ios_app_ids, android_apps, login_enabled, aaguid_allowlist, and aaguid_blocklist keep their stored value when you omit them.
Add the fingerprint of every certificate that signs a build your users install. A debug build and a Play Store build are usually signed with different certificates.
Declare the relying-party host in your app
- iOS
- Android
- Flutter
- React Native
Add the Associated Domains capability to your app target, with one entry for the relying-party host:
Register a passkey
A passkey is never the first credential of a user. Registration runs inside an authenticated session and requires theprld:passkey:write scope, which a step-up grants. Complete the step-up first, then register.
- iOS
- Android
- Flutter
- React Native
A user can register several passkeys. Each registration consumes the
prld:passkey:write scope: run a new step-up before the next registration, and before you list, rename, or delete passkeys.
When the device already holds a passkey for this user, a new registration on Android (also through Flutter and React Native) throws the “passkey already registered” error (see Errors). alreadyRegistered is true only when the authenticator returns a credential that Prelude already stores.
Sign in with a passkey
loginWithPasskey signs a user in with the platform authenticator. The app sends no identifier: the authenticator offers the passkeys it holds for the relying party, and the server resolves the user from the one the user picks. On success the SDK persists the session tokens in the platform’s secure store.
Requires login_enabled in the passkey configuration.
- iOS
- Android
- Flutter
- React Native
AutoFill (iOS)
On iOS, theautofill option offers matching passkeys in the keyboard’s AutoFill row instead of a modal sheet, when the user focuses a username field. The request shows no sheet and stays active, so start it when your sign-in screen appears. Android always shows the Credential Manager sheet.
- iOS
- Android
- Flutter
- React Native
Use a passkey in a step-up
When a step-up challenge hasverify_passkey as its current step, advance it with the platform authenticator instead of a code. continueStepUpWithPasskey has the same contract as submitStepUpOTP: it returns the next challenge, or nothing when the step-up is complete.
- iOS
- Android
- Flutter
- React Native
Manage registered passkeys
Build a “Manage your passkeys” screen with three methods. All three requireprld:passkey:write, the same scope as registration, so run the step-up before you open the screen. A registration consumes the scope, so run the step-up again after the user adds a passkey from this screen. Listing returns an empty list when the user has no passkey. Pass an empty nickname to clear the label.
- iOS
- Android
- Flutter
- React Native
nickname, the transports reported by the authenticator, the backupState (whether the passkey is synced), and the creation and last-use times.
Errors
Each SDK names these errors in its own convention, for example
PreludeAuthError.passkeyNotSupported (iOS), PreludeAuthError.PasskeyNotSupported (Android), PasskeyNotSupportedException (Flutter), and PasskeyNotSupportedError (React Native).
If a ceremony fails although the code is correct, check the platform setup first: a missing association on either side makes the operating system refuse the ceremony before it reaches Prelude.