Passkeys provide a secure, passwordless authentication method that can be used to create wallets for your users without going through email verification flows. You can implement passkey signup with or without an associated email address.
If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device.
Recommended security practice: Proxy authentication requests to your backend server to enforce additional security measures:
When a user attempts to sign up with both passkey and email, you can first require email verification before allowing the passkey to be created
Alternatively, you can restrict initial signup to email-based methods only (which inherently verify email ownership), then allow users to add passkeys after their account is established
This approach gives you greater control over the authentication flow and helps prevent account recovery issues
By implementing server-side verification, you ensure that passkeys are only created for verified identities, reducing the risk of permanent access loss.
You can implement Passkey Signup authentication in two ways:
After adding the components, configure the Passkey Signup authentication in your application config:
import { AlchemyAccountsUIConfig, createConfig } from "@account-kit/react";import { sepolia, alchemy } from "@account-kit/infra";const uiConfig: AlchemyAccountsUIConfig = { auth: { sections: [ [ // Include passkey in a section { type: "passkey" }, // You can combine with other authentication methods { type: "email" }, ], ], // Enable automatic passkey creation after signup addPasskeyOnSignup: true, },};export const config = createConfig( { transport: alchemy({ apiKey: "your-api-key" }), chain: sepolia, }, uiConfig,);
If you need complete control over the user experience, you can implement your own custom UI for Passkey Signup authentication using Smart Wallets hooks.
This approach associates an email with the passkey, allowing users to recover their account if they lose access to their device.
import { useAuthenticate } from "@account-kit/react";// Inside your componentconst { authenticate } = useAuthenticate();// When the user submits their email and wants to create a passkeyconst handlePasskeySignup = (email: string) => { // Important: Validate the email before proceeding if (!isValidEmail(email)) { // Handle validation error return; } authenticate( { type: "passkey", email, }, { onSuccess: () => { // Success - passkey created and user authenticated }, onError: (error) => { // Handle error }, }, );};// Simple email validation functionconst isValidEmail = (email: string) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);};
It's important that you validate the email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
This approach creates a passkey without an associated email. Use this only if you have another recovery mechanism in place.
import { useAuthenticate } from "@account-kit/react";// Inside your componentconst { authenticate } = useAuthenticate();// When the user wants to create a passkey without emailconst handlePasskeyOnlySignup = (username: string) => { authenticate( { type: "passkey", createNew: true, username, // A unique identifier for the passkey }, { onSuccess: () => { // Success - passkey created and user authenticated }, onError: (error) => { // Handle error }, }, );};
Use the useSignerStatus hook to determine if the user is authenticated:
import { useSignerStatus } from "@account-kit/react";// Inside your componentconst { isConnected } = useSignerStatus();// You can use isConnected to conditionally render UI
This guide assumes you have already followed the Setup
Guide and have set up the Alchemy
Account Provider using this
guide.
Please refer to the guides above for more information on how to properly setup
your project.
For a complete example of how we can setup a project and use the various
available authentication methods, please refer to our quickstart
example.
Authenticating a user is easy using the useAuthenticate() hook from the @account-kit/react-native package. Before we can use that, you'll need to configure your application to associate it with a domain you control.
The rpId ("relaying party ID") specifies the domain on which passkeys are allowed to function. While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered with a domain to prove that they are associated.
In your call to createConfig, pass an rpId parameter set to a domain you control. Note that the scheme is always assumed to be "https://" and should be omitted.
const config = createConfig({ // ... other config rpId: "your-domain.com",});
While passkeys on web applications are automatically associated with the website's domain, mobile applications must be registered on that domain to prove that they are associated. To do so, you will need to host a JSON file referencing your app on your domain. The details of doing so differ on iOS and Android.
GET https://<yourdomain>/.well-known/apple-app-site-association
This route should serve a static JSON object containing your team id and identifier. You should replace <team-identifier> and <bundle-id> in the below snippet, so it might appear as e.g. H123456789.com.yourapp.passkeyExample.
Next, in XCode under "Signing & Capabilities", add a new capability of type "Associated Domains". Now add the following, replacing <yourdomain> with the domain on which you hosted the JSON (e.g. your-domain.com):
You should replace <your-package-name> with the package name, e.g. com.yourapp.passkeyExample, and "<sha-hex-value>" with the SHA256 fingerprints of your app's [signing certificate], e.g. "FA:C6:17:45:DC:09:03:78:6F:B9:ED:E6:2A:96:2B:39:9F:73:48:F0:BB:6F:89:9B:83:32:12:75:91:03:3B:9C".
You have the option of creating an account with passkey and email or with passkey alone.
If you create a passkey without an email associated with the user, you risk your users losing access to their wallets if they lose their device.
Recommended security practice: Proxy authentication requests to your backend server to enforce additional security measures:
When a user attempts to sign up with both passkey and email, you can first require email verification before allowing the passkey to be created
Alternatively, you can restrict initial signup to email-based methods only (which inherently verify email ownership), then allow users to add passkeys after their account is established
This approach gives you greater control over the authentication flow and helps prevent account recovery issues
By implementing server-side verification, you ensure that passkeys are only created for verified identities, reducing the risk of permanent access loss.
import { useAuthenticate } from "@account-kit/react-native";import React, { useState } from "react";import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";function CreatePasskeyAndEmail() { const { authenticate } = useAuthenticate(); const [email, setEmail] = useState(""); const handleCreatePasskeyAndEmail = () => { // Important: Validate the email before proceeding if (!isValidEmail(email)) { // Handle validation error return; } try { authenticate({ type: "passkey", email, }); // Prompt the user to create a passkey, and create an account once they do. } catch (e) { Alert.alert("Error creating passkey. Check logs for more details."); console.log("Error creating passkey: ", e); } }; return ( <View> <Text>Enter Your Email to Create Account</Text> <View> <TextInput value={email} onChangeText={(val) => setEmail(val.toLowerCase())} placeholder="[email protected]" /> <Pressable onPress={handleCreatePasskeyAndEmail}> {({ pressed }) => ( <View style={[ { opacity: pressed ? 0.5 : 1, transform: [ { scale: pressed ? 0.98 : 1, }, ], }, ]} > <Text>Sign In</Text> </View> )} </Pressable> </View> </View> );}// Simple email validation functionconst isValidEmail = (email: string) => { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);};
It's important that you validate the email before creating an account for the
user. This is to prevent users from losing access to their wallets if they
lose their device.
To create an account with a passkey, use the authenticate() function, with the type set to "passkey" and createNew set to true.
import { useAuthenticate } from "@account-kit/react-native";import React, { useState } from "react";import { Alert, View, Text, TextInput, Button, Pressable } from "react-native";function CreatePasskey() { const { authenticate } = useAuthenticate(); const handleCreatePasskey = () => { try { authenticate({ type: "passkey", createNew: true, // This will be the name of the saved passkey on the user's device. username: "Your App user", }); } catch (e) { Alert.alert("Error creating passkey. Check logs for more details."); console.log("Error creating passkey: ", e); } }; return ( <View> <Text>Create an account with a passkey</Text> <View> <Pressable onPress={handleCreatePasskey}> {({ pressed }) => ( <View style={[ { opacity: pressed ? 0.5 : 1, transform: [ { scale: pressed ? 0.98 : 1, }, ], }, ]} > <Text>Create account</Text> </View> )} </Pressable> </View> </View> );}
To sign in with an existing passkey, use the authenticate() function, with the type set to "passkey".
It is possible to create wallets for users using just a passkey. This is useful for creating wallets for users if you don't want to go through the email OTP or magic link flow.
If you want to allow sign-up and login with a passkey, you can ask the user for an email to associate with their passkey. This way, they can log in with their email or passkey in the future. Under the hood, the email is also used to check if an account exists already so you can have a unified sign-up and login flow.
It's important that you validate this email before creating an account for the user. This is to prevent users from losing access to their wallets if they lose their device.
import { signer } from "./signer";const result = await signer.authenticate({ type: "passkey", email: "[email protected]",});