Skip to main content
React Native needs native platform configuration before any passkey call works, and it wires connections a little differently from the web. This guide covers both.

Native prerequisites (required)

An rpId is a domain claim, and the OS honors it only if the domain claims your app back. This is platform configuration, not Avok configuration; a passkey call fails at the OS layer without it.
  • iOS: add the webcredentials:<your-rpId> entitlement (Associated Domains) and serve /.well-known/apple-app-site-association from that domain with your app’s identifier.
  • Android: serve /.well-known/assetlinks.json from that domain with your package name and signing-certificate fingerprint (Digital Asset Links).
In Expo, both are config-plugin territory (app.json associatedDomains and the Android intent-filter setup); you cannot set them from JavaScript. The rpId you pass must be that same domain.

Own-origin

Inject the passkey module and use SecureStore for non-secret state:

Shared-origin

Shared-origin runs the ceremony in an in-app browser tab and brings back only the result. Use createNativeSharedOrigin, providing an openAuthSession that matches expo-web-browser’s openAuthSessionAsync:
Unlike the web popup, the callback URL carries no origin authenticity, so the account self-authenticates: connect() verifies a signature over a caller nonce before trusting the account. Keep returned payloads to a few kilobytes. Android’s Binder transaction buffer is 1 MB and shared across every transaction in the process, so how much room a payload actually has depends on what else the app is doing at the time, not on the payload alone.

Device pairing

usePairingCeremony runs the pairing phase machine over an injected transport (transport is required on React Native, which ships no camera view). createExpoCameraTransport bridges expo-camera:
Render a QR of transport.currentCode and feed scanned barcodes with transport.feedBarcode. See Enroll a device.