> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Passkey Signer

> Configure a passkey signer at wallet creation using WebAuthn biometric authentication.

Use a passkey signer to let users sign transactions with biometric authentication — Face ID, Touch ID, fingerprint, or Windows Hello. Passkeys are built on the <a href="https://www.w3.org/TR/webauthn-3/" target="_blank">WebAuthn</a> standard and are synchronized across devices via providers like Apple Keychain, Google Password Manager, and 1Password.

## Supported Chains

| Chain | Operational signer | Recovery method |
| - | - | - |
| EVM | ✅ | ✅ |
| Stellar | ✅ | ✅ |
| Solana | ✅ | ❌ |

## Configuration

Configure the wallet to use a passkey as the operational signer at creation time. The SDK handles WebAuthn registration during the first wallet creation.

<Tabs>
  <Tab title="React">
    Using `createOnLogin` on the provider:

    ```tsx theme={null}
    import {
        CrossmintProvider,
        CrossmintAuthProvider,
        CrossmintWalletProvider,
    } from "@crossmint/client-sdk-react-ui";

    function App({ children }) {
        return (
            <CrossmintProvider apiKey="YOUR_CLIENT_API_KEY">
                <CrossmintAuthProvider loginMethods={["email", "google"]}>
                    <CrossmintWalletProvider
                        createOnLogin={{
                            chain: "base-sepolia",
                            recoveryMethods: [{ type: "email" }],
                            signers: [{ type: "passkey" }],
                        }}
                    >
                        {children}
                    </CrossmintWalletProvider>
                </CrossmintAuthProvider>
            </CrossmintProvider>
        );
    }
    ```

    Or create the wallet explicitly:

    ```typescript theme={null}
    import { useWallet } from "@crossmint/client-sdk-react-ui";

    const { createWallet } = useWallet();

    const wallet = await createWallet({
        chain: "base-sepolia",
        recoveryMethods: [{ type: "email", email: "user@example.com" }],
        signers: [{ type: "passkey" }],
    });
    ```

    The same configuration works on Stellar and Solana: set `chain` to `"stellar"` or `"solana"`. On Stellar a passkey can also be a recovery method (`recoveryMethods: [{ type: "passkey" }]`); on Solana it can only be an operational signer.

    Each transaction triggers a biometric authentication prompt.
  </Tab>

  <Tab title="React Native">
    React Native uses the platform passkey APIs (iOS AuthenticationServices, Android Credential Manager) through the <a href="https://github.com/f-23/react-native-passkey" target="_blank">`react-native-passkey`</a> package.

    <Steps>
      <Step title="Install the native module">
        Install `react-native-passkey` and rebuild the app. Passkeys need a development build; Expo Go cannot run native modules.

        ```bash theme={null}
        npx expo install react-native-passkey
        ```
      </Step>

      <Step title="Associate the app with the relying party domain">
        Associate the app with the domain you use as the relying party ID (`rpId`):

        * **iOS:** add the associated domain `webcredentials:app.example.com` to the app, and serve an `apple-app-site-association` file from the domain that lists the app.
        * **Android:** serve `/.well-known/assetlinks.json` from the domain with the `delegate_permission/common.get_login_creds` relation for the app's package name and signing certificate.
      </Step>

      <Step title="Configure passkeys on the wallet provider">
        Pass `passkeys` to `CrossmintWalletProvider`. Pass `Passkey` from `react-native-passkey` so your bundler links the native module:

        ```tsx theme={null}
        import { Passkey } from "react-native-passkey";
        import {
            CrossmintProvider,
            CrossmintAuthProvider,
            CrossmintWalletProvider,
        } from "@crossmint/client-sdk-react-native-ui";

        function App({ children }) {
            return (
                <CrossmintProvider apiKey="YOUR_CLIENT_API_KEY">
                    <CrossmintAuthProvider loginMethods={["email", "google"]}>
                        <CrossmintWalletProvider
                            passkeys={{ rpId: "app.example.com", rpName: "Example", passkey: Passkey }}
                            createOnLogin={{
                                chain: "base-sepolia",
                                recoveryMethods: [{ type: "email" }],
                                signers: [{ type: "passkey" }],
                            }}
                        >
                            {children}
                        </CrossmintWalletProvider>
                    </CrossmintAuthProvider>
                </CrossmintProvider>
            );
        }
        ```
      </Step>
    </Steps>

    <Warning>
      Keep `rpId` fixed once users have created passkeys. A passkey only works for the domain it was created for.
    </Warning>

    Without the `passkeys` prop, the React Native SDK rejects passkey signers. Every passkey operation requires user verification (biometrics or a device PIN).
  </Tab>

  <Tab title="Swift">
    The Swift SDK uses the iOS passkey APIs (AuthenticationServices) and shows the system prompts for you. The steps below need a signed-in user; see the [Swift quickstart](/wallets/quickstarts/swift) to configure the SDK and sign the user in.

    <Steps>
      <Step title="Associate the app with the relying party domain">
        Add the associated domain `webcredentials:app.example.com` to the app, and serve an `apple-app-site-association` file from the domain that lists the app. The SDK uses this domain (`host`) as the relying party ID.
      </Step>

      <Step title="Create the wallet with a passkey signer">
        Pass a passkey in `signers`. The SDK asks the user to create the passkey, then creates the wallet:

        ```swift theme={null}
        import CrossmintClient

        CrossmintSDK.configure(apiKey: "YOUR_CLIENT_API_KEY")
        let sdk = CrossmintSDK.shared

        let passkey = SignerConfig.passkey(name: "user@example.com", host: "app.example.com")

        let wallet = try await sdk.crossmintWallets.createWallet(
            chain: EVMChain.baseSepolia,
            recoveryMethods: [EVMSigners.email("user@example.com")],
            signers: [passkey]
        )
        try await wallet.useSigner(passkey)
        ```

        `name` is the account name that the passkey prompt shows.
      </Step>

      <Step title="Sign with the passkey on later launches">
        Set `passkeyHost` when you get the wallet. The SDK then signs with the passkey:

        ```swift theme={null}
        // Uses the SDK configured in the create step
        import CrossmintClient

        let wallet = try await CrossmintSDK.shared.crossmintWallets.getWallet(
            chain: EVMChain.baseSepolia,
            options: WalletOptions(passkeyHost: "app.example.com")
        )
        ```
      </Step>
    </Steps>

    The same configuration works on Stellar and Solana: use `StellarChain.stellar` or `SolanaChain.solana`. On Stellar a passkey can also be a recovery method (`StellarSigners.passkey(name:host:)`); on Solana it can only be a signer.
  </Tab>

  <Tab title="Kotlin">
    The Kotlin SDK uses Android Credential Manager through the `sdk-passkeys` module. The steps below need a configured SDK and a signed-in user; see the [Kotlin quickstart](/wallets/quickstarts/kotlin).

    <Steps>
      <Step title="Add the passkeys module">
        The `crossmint-sdk` artifact does not include passkey support. Add `sdk-passkeys` with the same version:

        ```kotlin build.gradle.kts theme={null}
        dependencies {
            implementation(libs.crossmint.sdk)
            implementation(libs.crossmint.sdk.passkeys)
        }
        ```

        ```toml libs.versions.toml theme={null}
        [libraries]
        crossmint-sdk-passkeys = { module = "com.crossmint:sdk-passkeys", version.ref = "crossmint-sdk" }
        ```
      </Step>

      <Step title="Associate the app with the relying party domain">
        Serve `/.well-known/assetlinks.json` from the domain you use as the relying party ID (`rpId`), with the `delegate_permission/common.get_login_creds` relation for the app's package name and signing certificate.
      </Step>

      <Step title="Create the wallet with a passkey signer">
        Create the passkey with `createPasskeyCredential`, and pass it in `signers`. `AndroidPasskeyProvider` shows the passkey prompt each time the wallet signs:

        ```kotlin theme={null}
        import android.app.Activity
        import androidx.credentials.CredentialManager
        import com.crossmint.kotlin.CrossmintSDK
        import com.crossmint.kotlin.passkeys.AndroidPasskeyProvider
        import com.crossmint.kotlin.passkeys.createPasskeyCredential
        import com.crossmint.kotlin.signers.DelegatedSigner
        import com.crossmint.kotlin.signers.SignerType
        import com.crossmint.kotlin.types.EVMChain
        import com.crossmint.kotlin.types.getOrThrow

        suspend fun createPasskeyWallet(activity: Activity) {
            val credential = createPasskeyCredential(
                name = "user@example.com",
                activityProvider = { activity },
                credentialManager = CredentialManager.create(activity),
                rpId = "app.example.com",
                rpName = "Example",
            ) ?: return

            val wallet = CrossmintSDK.shared.wallets.createWallet(
                chain = EVMChain.BaseSepolia,
                recoveryMethods = listOf(SignerType.Email("user@example.com")),
                signers = listOf(
                    DelegatedSigner.Passkey(
                        id = credential.id,
                        name = credential.name,
                        publicKeyX = credential.publicKeyX,
                        publicKeyY = credential.publicKeyY,
                    ),
                ),
                passkeyProvider = AndroidPasskeyProvider(rpId = "app.example.com"),
            ).getOrThrow()
        }
        ```

        `createPasskeyCredential` returns `null` when the user cancels or the passkey cannot be created.
      </Step>

      <Step title="Sign with the passkey on later launches">
        Pass an `AndroidPasskeyProvider` when you get the wallet. The SDK then signs with the passkey:

        ```kotlin theme={null}
        // Uses the SDK configured at app start, as in the create step
        import com.crossmint.kotlin.CrossmintSDK
        import com.crossmint.kotlin.passkeys.AndroidPasskeyProvider
        import com.crossmint.kotlin.types.EVMChain
        import com.crossmint.kotlin.types.getOrThrow

        val wallet = CrossmintSDK.shared.wallets.getWallet(
            chain = EVMChain.BaseSepolia,
            passkeyProvider = AndroidPasskeyProvider(rpId = "app.example.com"),
        ).getOrThrow()
        ```
      </Step>
    </Steps>

    The same configuration works on Stellar and Solana: use `StellarChain.Stellar` or `SolanaChain.Solana`. On Stellar a passkey can also be a recovery method (`SignerType.Passkey`); on Solana it can only be a signer.
  </Tab>
</Tabs>

## Adding a Passkey to an Existing Wallet

If the wallet already exists with a different signer, you can add a passkey with `addSigner`. See [Add Signers](/wallets/guides/signers/add-signers#add-a-passkey-signer) for the full flow.

## Next Steps

<CardGroup cols={3}>
  <Card title="Add Signers" icon="key" href="/wallets/guides/signers/add-signers">
    Register additional signers on an existing wallet
  </Card>

  <Card title="Configure Recovery" icon="shield-halved" href="/wallets/guides/recovery/configure-recovery">
    Set up email or phone recovery
  </Card>

  <Card title="Device Signer" icon="microchip" href="/wallets/guides/signers/device-signer">
    The default client-side signer
  </Card>
</CardGroup>
