{"id":17602,"library":"expo-better-auth-passkey","title":"Expo Better Auth Passkey Integration","description":"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.","status":"active","version":"1.4.1","language":"javascript","source_language":"en","source_url":"https://github.com/kevcube/expo-better-auth-passkey","tags":["javascript","react-native","expo","expo-better-auth-passkey","better-auth","passkey","webauthn","typescript"],"install":[{"cmd":"npm install expo-better-auth-passkey","lang":"bash","label":"npm"},{"cmd":"yarn add expo-better-auth-passkey","lang":"bash","label":"yarn"},{"cmd":"pnpm add expo-better-auth-passkey","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core authentication framework this package extends and integrates with.","package":"better-auth","optional":false},{"reason":"The underlying passkey plugin it replaces/wraps for native functionality.","package":"@better-auth/passkey","optional":false},{"reason":"Required for Expo application environment and native module linking.","package":"expo","optional":false},{"reason":"Fundamental library for React Native components.","package":"react","optional":false},{"reason":"Core framework for building native mobile applications.","package":"react-native","optional":false},{"reason":"Required by `better-auth` and used implicitly by its plugin architecture.","package":"nanostores","optional":false}],"imports":[{"note":"This is the primary named export for integrating native passkey support. Ensure you use ES module import syntax for modern React Native/Expo development.","wrong":"const expoPasskeyClient = require('expo-better-auth-passkey');","symbol":"expoPasskeyClient","correct":"import { expoPasskeyClient } from 'expo-better-auth-passkey';"},{"note":"While not directly from `expo-better-auth-passkey`, `createAuthClient` from `better-auth/react` is essential for instantiating the auth client that uses `expoPasskeyClient`. It is a named export.","wrong":"import createAuthClient from 'better-auth/react';","symbol":"createAuthClient","correct":"import { createAuthClient } from 'better-auth/react';"},{"note":"For type-checking the passkey plugin object returned by `expoPasskeyClient()`, import `PasskeyPlugin` as a type from the core `@better-auth/passkey` package. `expoPasskeyClient` returns an object compatible with this plugin type.","wrong":"import { PasskeyPlugin } from '@better-auth/passkey';","symbol":"PasskeyPlugin","correct":"import type { PasskeyPlugin } from '@better-auth/passkey';"}],"quickstart":{"code":"import { createAuthClient } from 'better-auth/react';\nimport { expoPasskeyClient } from 'expo-better-auth-passkey';\nimport { Platform } from 'react-native';\n\n// Replace with your actual backend URL where Better Auth server is running\nconst BASE_URL = process.env.BETTER_AUTH_API_URL || 'https://your-api.mydomain.com';\n\n// Initialize the Better Auth client with the Expo Passkey plugin\nexport const authClient = createAuthClient({\n  baseURL: BASE_URL,\n  plugins: [\n    expoPasskeyClient(),\n    // Add other Better Auth client plugins as needed (e.g., email, session)\n  ],\n});\n\nasync function handlePasskeyOperations() {\n  try {\n    console.log(`Running on platform: ${Platform.OS}`);\n\n    // Example: Register a new passkey with a descriptive name\n    const addPasskeyResult = await authClient.passkey.addPasskey({ name: `My ${Platform.OS} Passkey` });\n    console.log('Passkey registered successfully:', addPasskeyResult);\n\n    // Example: Sign in using an existing passkey\n    // In a real application, you might first get the user's identifier (e.g., email)\n    // before attempting passkey sign-in.\n    const signInResult = await authClient.signIn.passkey({ email: 'user@example.com' });\n    console.log('Signed in with passkey successfully:', signInResult);\n  } catch (error) {\n    console.error('Passkey operation failed:', error);\n    // Implement robust error handling, e.g., user cancellation, network issues,\n    // or specific platform errors related to passkey APIs.\n  }\n}\n\n// Execute the passkey operations\nhandlePasskeyOperations();","lang":"typescript","description":"Initializes a Better Auth client with native passkey support for Expo apps and demonstrates basic passkey registration and sign-in operations across supported platforms."},"warnings":[{"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()`.","message":"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.","severity":"breaking","affected_versions":">=1.2.0"},{"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`.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"},{"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.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"},{"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`.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0"},{"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.","message":"Passkey operations fundamentally require a secure origin (HTTPS). Development environments should use tunneling services like ngrok or localhost HTTPS setup to avoid security errors.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Upgrade to `expo-better-auth-passkey@1.4.1` or newer to ensure correct `presentationAnchor` behavior and improve stability for native passkey interactions.","message":"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.","severity":"gotcha","affected_versions":">=1.0.0 <1.4.1"}],"env_vars":null,"last_verified":"2026-04-23T00:00:00.000Z","next_check":"2026-07-22T00:00:00.000Z","problems":[{"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`.","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.","error":"ASAuthorizationError.Code.notKnown | ASAuthorizationError.Code.canceled | ASAuthorizationError.Code.invalidRequest on iOS"},{"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.","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.","error":"Passkey operation failed due to invalid relying party ID or origin configuration."},{"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.","cause":"Missing or incorrect `android:apk-key-hash` configuration on the Better Auth server for your Android app's signing certificate.","error":"SecurityException: Cannot use Credential Manager without a valid relying party ID configured / 'No Android package hash supplied in WebAuthn attestation options.'"},{"fix":"Ensure `expoPasskeyClient()` is included in the `plugins` array passed to `createAuthClient({ plugins: [...] })` and that `authClient` is initialized before attempting passkey operations.","cause":"The `expoPasskeyClient()` plugin was not correctly added to the `plugins` array when calling `createAuthClient()`, or `authClient` was not properly initialized.","error":"TypeError: Cannot read properties of undefined (reading 'passkey') or 'addPasskey' of undefined"}],"ecosystem":"npm","meta_description":null,"install_score":null,"install_tag":null,"quickstart_score":null,"quickstart_tag":null}