Expo Better Auth Passkey Integration

raw JSON →
1.4.1 verified Thu Apr 23 auth: no javascript

The `expo-better-auth-passkey` package provides native passkey (WebAuthn) support for applications built with Expo and `better-auth`. It acts as a drop-in replacement for `better-auth`'s standard `passkeyClient`, leveraging platform-specific APIs like Apple's `ASAuthorizationController` for iOS/macOS and Android's Credential Manager. This ensures a consistent passkey experience across web, iOS, and Android using a single codebase. The library, currently at version 1.4.1, maintains an active release schedule with updates addressing bug fixes and new features. Key differentiators include its seamless integration into both managed and bare Expo projects without requiring ejecting, a smart fallback to the web client for web builds, and a TypeScript-first approach with strict type mirroring for robust development.

error ASAuthorizationError.Code.notKnown | ASAuthorizationError.Code.canceled | ASAuthorizationError.Code.invalidRequest on iOS
cause iOS Associated Domains capability is not enabled in Xcode/app.json, or the `apple-app-site-association` file is misconfigured/missing on the server.
fix
Verify ios.associatedDomains in app.json includes webcredentials:your-rp-id.com and that the apple-app-site-association file is correctly hosted and accessible at https://your-rp-id.com/.well-known/apple-app-site-association.
error Passkey operation failed due to invalid relying party ID or origin configuration.
cause Mismatch between client-side `baseURL`/`rpID` and server-side `rpID`/`origin` parameters, or the client's origin is not in the server's `trustedOrigins` list.
fix
Cross-reference rpID, rpName, and origin values on your Better Auth server configuration with your app's domain. Ensure all client app schemes (e.g., myapp://, https://localhost) are explicitly included in trustedOrigins on the server.
error SecurityException: Cannot use Credential Manager without a valid relying party ID configured / 'No Android package hash supplied in WebAuthn attestation options.'
cause Missing or incorrect `android:apk-key-hash` configuration on the Better Auth server for your Android app's signing certificate.
fix
Generate the correct Base64 SHA256 hash of your Android app's signing certificate and add it to the android:apk-key-hash array in your Better Auth server passkey plugin configuration.
error TypeError: Cannot read properties of undefined (reading 'passkey') or 'addPasskey' of undefined
cause The `expoPasskeyClient()` plugin was not correctly added to the `plugins` array when calling `createAuthClient()`, or `authClient` was not properly initialized.
fix
Ensure expoPasskeyClient() is included in the plugins array passed to createAuthClient({ plugins: [...] }) and that authClient is initialized before attempting passkey operations.
breaking The parameter structure for `registerPasskey` (now `addPasskey`) and `authenticatePasskey` (now `signIn.passkey`) functions was updated in `v1.2.0`. Projects upgrading from earlier versions must review their usage of these methods.
fix Review the official `better-auth` and `expo-better-auth-passkey` documentation for the updated parameter structures, specifically for `authClient.passkey.addPasskey()` and `authClient.signIn.passkey()`.
gotcha For iOS and macOS, the 'Associated Domains' capability must be enabled in your Xcode project or via `expo prebuild` config (`ios.associatedDomains`), and an `apple-app-site-association` file must be correctly hosted on your relying party domain.
fix Add `webcredentials:your-auth-domain.com` to `ios.associatedDomains` in your `app.json` and ensure the `apple-app-site-association` file is correctly hosted and accessible at `https://your-auth-domain.com/.well-known/apple-app-site-association`.
gotcha On Android, the Credential Manager APIs used for native passkeys require Google Play Services version 23.30 or newer. Older device versions will not support native passkey operations.
fix Inform users about the Google Play Services requirement or gracefully degrade to alternative authentication methods if the device doesn't meet the minimum version.
gotcha Your Better Auth server must be configured correctly for passkeys, including `rpID`, `rpName`, `origin`, and crucially, `android:apk-key-hash` entries for Android builds and a comprehensive `trustedOrigins` list for all client entry points.
fix Verify `rpID` matches your domain, `origin` is correct, and add `android:apk-key-hash:<BASE64_SHA256>` for all signing certificates to your Better Auth server passkey plugin configuration. Include all app schemes (e.g., `myapp://`, `https://localhost`) in `trustedOrigins`.
gotcha Passkey operations fundamentally require a secure origin (HTTPS). Development environments should use tunneling services like ngrok or localhost HTTPS setup to avoid security errors.
fix Ensure your `baseURL` and `origin` configurations on both client and server sides use HTTPS. For local development, set up HTTPS for your local server or use a tunnel for your API endpoint.
gotcha A bug fix in v1.4.1 addressed an issue where `presentationAnchor` was not reliably running on the main thread, which could lead to UI freezes or native API call failures on older versions.
fix Upgrade to `expo-better-auth-passkey@1.4.1` or newer to ensure correct `presentationAnchor` behavior and improve stability for native passkey interactions.
npm install expo-better-auth-passkey
yarn add expo-better-auth-passkey
pnpm add expo-better-auth-passkey

Initializes a Better Auth client with native passkey support for Expo apps and demonstrates basic passkey registration and sign-in operations across supported platforms.

import { createAuthClient } from 'better-auth/react';
import { expoPasskeyClient } from 'expo-better-auth-passkey';
import { Platform } from 'react-native';

// Replace with your actual backend URL where Better Auth server is running
const BASE_URL = process.env.BETTER_AUTH_API_URL || 'https://your-api.mydomain.com';

// Initialize the Better Auth client with the Expo Passkey plugin
export const authClient = createAuthClient({
  baseURL: BASE_URL,
  plugins: [
    expoPasskeyClient(),
    // Add other Better Auth client plugins as needed (e.g., email, session)
  ],
});

async function handlePasskeyOperations() {
  try {
    console.log(`Running on platform: ${Platform.OS}`);

    // Example: Register a new passkey with a descriptive name
    const addPasskeyResult = await authClient.passkey.addPasskey({ name: `My ${Platform.OS} Passkey` });
    console.log('Passkey registered successfully:', addPasskeyResult);

    // Example: Sign in using an existing passkey
    // In a real application, you might first get the user's identifier (e.g., email)
    // before attempting passkey sign-in.
    const signInResult = await authClient.signIn.passkey({ email: 'user@example.com' });
    console.log('Signed in with passkey successfully:', signInResult);
  } catch (error) {
    console.error('Passkey operation failed:', error);
    // Implement robust error handling, e.g., user cancellation, network issues,
    // or specific platform errors related to passkey APIs.
  }
}

// Execute the passkey operations
handlePasskeyOperations();