Skip to main content
The video uses a specific AI coding tool to demonstrate the setup, but the configuration works 1:1 with Cursor, Claude Code, or any other tool. Despia is web framework and tooling agnostic, so the only thing that matters is the SDK call.
This bioauth:// scheme is the legacy biometric API, kept available for apps already shipping with it. New apps should use Storage Vault instead. Storage Vault binds biometric authentication to encrypted token storage backed by iCloud Key Value Store on iOS and Android Key/Value Backup on Android, which means tokens survive uninstall and sync across devices on the same Apple ID or Google account. The legacy bioauth:// only verifies client-side, it does not store anything. For any flow that ends with “and now I have a session token”, Storage Vault is the better tool.
despia('bioauth://') triggers the system biometric prompt. The runtime then calls one of three globals on window: onBioAuthSuccess(), onBioAuthFailure(errorCode, errorMessage), or onBioAuthUnavailable(). Define all three before triggering the call.

Installation


How it works

Two pieces. First, attach the three callbacks to window so the runtime can find them. Second, trigger the prompt with despia('bioauth://'). The runtime detects which biometric method is available (Face ID, Touch ID, or device passcode) and presents the matching system UI.
The callbacks are global because the native runtime invokes them by name. Defining them inside a closure or module scope will not work, the runtime cannot reach them.

The three callback paths

onBioAuthUnavailable() matters more than it seems. A user with a brand new device, a borrowed device, or a device after a reset may not have Face ID or Touch ID configured. Treat this as a normal branch, not as an error.

Gate a sensitive action behind biometrics

The typical pattern is a “Confirm with Face ID” button on a sensitive action like a payment, a logout from all sessions, or an account deletion confirmation. Define the callbacks once on mount, then trigger the prompt from the button handler.
Re-binding the callbacks inside useEffect rather than at module scope keeps them tied to the component currently asking for authentication. If two screens both want biometric confirmation, the second one’s mount overwrites the first one’s callbacks, which is the correct behavior. The previous screen is no longer visible, so its handler should not fire.

Security model

This API verifies that the device’s owner is present, nothing more. It does not produce a token, does not sign anything, and does not communicate with your backend. Treat it as a client-side gate, not as authentication. For any flow where a successful biometric should grant access to a server-protected resource, pair it with a real auth system. The biometric proves the human in front of the phone is the same human who set up the device. Your backend still needs its own session, JWT, or cookie issued through a normal auth flow. Combine the two by gating the use of an existing session token behind biometrics, not by treating the biometric prompt itself as login. For apps that need biometric-gated access to secrets (session tokens, API keys, recovery phrases), Storage Vault is the right tool. Setting locked=true on a vault entry triggers Face ID or Touch ID before reading the value back, so the secret is never exposed to JavaScript without a successful biometric.

Resources

NPM Package

despia-native