npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lobehub/expo-better-auth-passkey

v1.0.1

Published

Expo native passkey support for better-auth

Readme

@lobehub/expo-better-auth-passkey

Expo/React Native drop-in replacement for the Better Auth passkeyClient that works everywhere Better Auth runs today: Web, Android, and iOS. macOS shares the same native implementation but still needs wider community testing—pull requests and reports are welcome.

Why this module

  • Drop-in client: swap passkeyClient() with expoPasskeyClient() and keep the exact same Better Auth API surface.
  • Native Credential APIs: wraps WebAuthn calls with ASAuthorizationController on Apple platforms and Android Credential Manager on Android.
  • Works with managed or bare Expo projects; no ejecting required.
  • Single code path for web builds—falls back to the stock Better Auth web client when Platform.OS === 'web'.
  • TypeScript-first with strict types mirrored from @simplewebauthn/types.

Supported platforms

| Platform | Status | Notes | | --- | --- | --- | | Web | ✅ | Uses Better Auth's default WebAuthn client | | iOS 15.1+ | ✅ | Uses ASAuthorizationPlatformPublicKeyCredentialProvider | | Android (Credential Manager) | ✅ | Requires Google Play Services 23.30+ | | macOS 12+ | ⚠️ Needs testing | Same native code path as iOS; please file issues/PRs |

Installation

Prerequisites

  1. A Better Auth server configured with the passkey plugin. Make sure the server runs on HTTPS with a hostname that matches the relying party ID (rpID).
  2. Expo SDK 49 or newer (tested with 54). For React Native CLI users, Expo Modules Autolinking must be set up.
  3. nanostores available in your app (Better Auth already depends on it).

Install the package

npm install @lobehub/expo-better-auth-passkey
# or
yarn add @lobehub/expo-better-auth-passkey
# or
bun add @lobehub/expo-better-auth-passkey

The native module is autolinked. If you use a bare/React Native CLI project, run npx pod-install after installing.

Usage

Replace the standard passkeyClient with expoPasskeyClient. Nothing else changes:

import { createAuthClient } from 'better-auth/react'
import { expoPasskeyClient } from '@lobehub/expo-better-auth-passkey'

export const authClient = createAuthClient({
  baseURL: 'https://your-api.mydomain.com',
  plugins: [
    expoPasskeyClient(),
    // ...the rest of your Better Auth client plugins
  ],
})

// Works exactly like Better Auth's stock client:
await authClient.passkey.addPasskey({ name: 'My iPhone' })
await authClient.signIn.passkey({ email: '[email protected]' })

The module internally forwards every server call to Better Auth and only overrides the WebAuthn credential creation/retrieval steps. Web builds automatically fall back to the original Better Auth WebAuthn implementation.

Server configuration checklist

  • Better Auth passkey plugin: Configure rpID, rpName, and origin to match the public domain your app will use. When you ship Android builds, add an android:apk-key-hash:<BASE64_SHA256> entry for every signing certificate so Better Auth can validate APK-originated passkey requests.
  • Trusted origins: Include all app schemes you intend to use, e.g. myapp://, https://localhost, and any Expo dev tunnels. Example:
    trustedOrigins: [
      'https://auth.example.com',
      'myapp://',
      'com.example.myapp://',
    ]
  • HTTPS only: Passkeys require secure origins. Use a tunneling service (ngrok, localhost HTTPS) during development.

Platform-specific setup

iOS (and macOS)

  1. Enable the Associated Domains capability in Xcode or via expo prebuild config (ios.associatedDomains).
  2. Add a webcredentials: entry for every relying party domain:
    {
      "expo": {
        "ios": {
          "associatedDomains": [
            "webcredentials:auth.example.com"
          ]
        }
      }
    }
  3. Host an apple-app-site-association file at https://auth.example.com/.well-known/apple-app-site-association with content similar to:
    {
      "applinks": { "apps": [], "details": [] },
      "webcredentials": {
        "apps": ["<TEAMID>.com.example.myapp"]
      }
    }
    • No file extension and served as application/json (or application/pkcs7-mime).
    • <TEAMID> is your Apple developer team ID; the bundle identifier must match your release build.
  4. Make sure your Info.plist allows the relying party hostname as an associated domain. Expo handles this automatically when associatedDomains is set.

Optional hints supported by this module:

  • Pass { useAutoRegister: true } to addPasskey to request the platform UI to suggest immediate passkey creation (iOS 16+).
  • Pass { autoFill: true } to signIn.passkey to allow autofill suggestions (iOS 16+).

Android

  1. Min requirements: Android 9 (API 28) or newer with Credential Manager 1.3.0+. Users need Google Play Services 23.30+ for passkeys.
  2. App signing SHA-256: Obtain your app signing certificate fingerprint. For debug builds:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android | grep 'SHA256:'

Replace this with your Play App Signing fingerprint for production. Convert each raw SHA-256 fingerprint to base64 and add `android:apk-key-hash:<BASE64_SHA256>` entries to the Better Auth `origin` array so the server trusts credentials coming from your APK.
5. **Optional**: If you want to forward your Android app's HTTPS origin when calling Credential Manager, request the `android.permission.CREDENTIAL_MANAGER_SET_ORIGIN` permission (API 34+). The module automatically falls back when the permission is missing, so you can skip it if you don't need per-domain attribution.
6. The Android bridge rewrites `user.displayName` to match `user.name` before presenting the system dialog so that each passkey nickname shows up without conflicting with the persistent Better Auth `displayName` field.
3. Host `https://auth.example.com/.well-known/assetlinks.json` with content:
 ```json
 [
   {
     "relation": ["delegate_permission/common.handle_all_urls"],
     "target": {
       "namespace": "android_app",
       "package_name": "com.example.myapp",
       "sha256_cert_fingerprints": [
         "12:34:56:...:AB"
       ]
     }
   }
 ]
  • package_name is your Android application ID.
  • Include every signing fingerprint you use (debug, release, Play signing).
  1. If you use Expo managed workflow, set android.package in app.json/app.config.js so autolinking matches the identifier above.
  2. Ensure the relying party hostname (rpID) exactly matches the host portion of your HTTPS domain (auth.example.com). The module automatically injects the origin field before returning to Better Auth.

Web

No additional setup beyond the regular Better Auth client. The plugin detects the web platform and hands control back to Better Auth's built-in WebAuthn flow.

Development workflow

  • npm run build – compile the TypeScript sources.
  • npm run lint – lint with Expo module preset.
  • npm run test – run the Expo module test runner.
  • cd example && npm install && npm start – launch the example app. Use npm run ios / npm run android from the example directory for device simulators.

Error handling & diagnostics

  • User cancellations surface as Better Auth errors with code AUTH_CANCELLED so you can display friendly UI.
  • Native errors are logged to the console for debugging. Use a production logging service to capture them on devices.
  • Android Credential Manager exceptions (NO_ACTIVITY, CREATE_ERROR, GET_ERROR) bubble through the returned error object—inspect result.error when debugging.

Contributing & macOS testing

macOS uses the same AuthenticationServices implementation as iOS but has limited coverage. If you can validate on macOS 12+, please open an issue or PR with results. Contributions for advanced features (cross-platform authenticators, passkey list management, web fallbacks) are encouraged.

  1. Fork the repo and install dependencies with npm install.
  2. Use npm run build before opening a PR to ensure the generated build/ output is up to date.
  3. Follow the lint/test scripts above. Please include repro steps for any passkey edge cases you fix.

License

MIT © LobeHub