SettleMint
API referenceTypeScript SDK

React Native

Use the DALP TypeScript SDK from an Expo app: supported versions, the transport to pass, where the session lives, and how passkeys reach the device.

Overview

An app talks to the platform the way a server does: it holds the session itself and sends it on every request. A phone is not a browser, though. It has no WebAuthn, its built-in fetch cannot read a response as it arrives, and anything in the app bundle ships to every user.

The SDK detects React Native from navigator.product and adjusts for all three. It keeps the session in its own cookie store rather than the operating system's cookie jar, it refuses an API key, and it accepts a passkey ceremony you supply.

Supported versions

RuntimeStatus
Expo SDK 57 (React Native 0.86, React 19.2)Supported. Pass fetch from expo/fetch.
Bare React Native 0.86Calls and sign-in work on the built-in fetch. Streams do not; see below.
Hermes, a simulator, a deviceNot exercised in the SDK's own tests. See What is tested.

Transport

Pass fetch from expo/fetch to both clients, and share one cookie store between them:

import { createCookieStoreFetch, createDalpClient, makeCookieStore } from "@settlemint/dalp-sdk";
import { createDalpAuthClient } from "@settlemint/dalp-sdk/auth";
import { fetch } from "expo/fetch";
import * as SecureStore from "expo-secure-store";

const url = "https://dalp.example.com";
const cookieStore = makeCookieStore((await SecureStore.getItemAsync("dalp.session")) ?? "");

const auth = await createDalpAuthClient({ url, cookieStore, fetch, passkey });
const dalp = createDalpClient({ baseUrl: url, fetch: createCookieStoreFetch(cookieStore, fetch) });

The SDK sends credentials: "omit" on React Native, so the operating system's cookie jar never holds a second copy of the session that a sign-out would leave behind.

React Native's built-in fetch is a polyfill over XMLHttpRequest. It resolves only once the whole response has arrived, so a streaming route cannot run on it. The SDK recognizes that fetch, passed directly or through createCookieStoreFetch, and fails the stream with sdk.stream.UNREADABLE_BODY before sending the request. A wrapper you write yourself hides it: the error then arrives only when the response ends, and a stream that stays open never ends. expo/fetch streams the body, so every route works on it.

Keeping the session

The cookie store lives in memory. To survive an app restart, write its header to secure storage after sign-in, sign-out, and any call that may rotate the session, and read it back at launch:

const saveSession = () =>
  cookieStore.isEmpty()
    ? SecureStore.deleteItemAsync("dalp.session")
    : SecureStore.setItemAsync("dalp.session", cookieStore.header());

The header is the session credential. Keep it in the platform keychain (expo-secure-store does), never in AsyncStorage or a log.

Passkeys

React Native has no navigator.credentials, so the auth client cannot run the WebAuthn ceremony itself. Pass a passkey ceremony instead. It has two functions, each taking the options JSON the platform issued and returning the credential JSON the operating system produced:

import type { DalpPasskeyCeremony } from "@settlemint/dalp-sdk/auth";
import { Passkey } from "react-native-passkey";

const passkey: DalpPasskeyCeremony = {
  authenticate: async (options) => toAssertion(await Passkey.get({ ...options, rpId: options.rpId ?? host })),
  register: async (options) => toAttestation(await Passkey.create(options)),
};

toAssertion and toAttestation are yours to write. They copy the module's result into the WebAuthn JSON shape: id, rawId, type: "public-key", response, and clientExtensionResults.

auth.signIn.passkey() and auth.passkey.addPasskey() then work as they do in a browser: the SDK fetches the options, your ceremony talks to the operating system, and the SDK sends the result for verification. A ceremony that throws, for example because the user cancelled, comes back as an AUTH_CANCELLED or REGISTRATION_CANCELLED error carrying the ceremony's own message, and nothing is sent for verification.

React Native sends no Origin header of its own, so the SDK sends your instance's origin on every call. Passkey verification reads it, and the platform serves a stream on a session only to a caller from the instance's own origin. Pass an origin entry in the auth client's headers to override it there.

The operating system only releases a passkey to an app the domain vouches for:

  • iOS. Add webcredentials:<host> to the app's associated domains and publish /.well-known/apple-app-site-association on the host. iOS signs the assertion for the instance's own origin, which the platform accepts.
  • Android. Publish /.well-known/assetlinks.json on the host for the app's package and signing certificate. Android signs the assertion for android:apk-key-hash:<hash>, which the platform does not accept yet, so passkey sign-in on Android fails verification until it does.

What is tested

The SDK's unit suite runs a React Native harness: the whatwg-fetch and abort-controller polyfills React Native ships, a native HTTP stack with its own cookie jar, a FileReader, no WebCrypto, and a model of expo/fetch. On that harness it covers password and passkey sign-in, passkey registration, a cached read replayed from a 304, a wallet-verified write, a stream on expo/fetch that the platform serves only to the instance's origin, the refused stream on the built-in fetch, and the refused API key.

The harness is Node, not Hermes, and nothing runs on a simulator or a device.

On this page