@fingerprint79/react-native
v0.0.10
Published
React Native binding for the Fingerprint Platform — JS hooks + native iOS (Swift) and Android (Kotlin) bridges. Collects mobile signals (IDFV, Keychain UUID, hardware) and ships sealed payloads to your self-hosted collector.
Maintainers
Readme
@fingerprint79/react-native
React Native binding for the Fingerprint Platform — a JS hooks API on top of native iOS (Swift) and Android (Kotlin) modules. Collects mobile signals (IDFV, Keychain UUID, ANDROID_ID, hardware facts), seals the payload with the same X25519 + AES-GCM crypto as the browser SDK, and POSTs to your self-hosted collector.
Sister packages:
fingerprint-platform-sdk— browser SDKfingerprint-platform-react— web React Provider + hooks (same API surface as this package)fingerprint-platform-mcp— Model Context Protocol server that installs all of the above into your project from Claude / Cursor
Install
npm install @fingerprint79/react-native \
@react-native-async-storage/async-storage \
react-native-localize \
@react-native-community/netinfoThen on iOS:
cd ios && pod installAndroid picks up the module automatically through Gradle autolinking on the next npx react-native run-android.
Requirements
- React Native 0.72+ (bare workflow — Expo not supported yet)
- iOS 12.0+ (Swift 5.7+)
- Android API 21+ (Android 5.0)
Usage
// App.tsx
import { FpProvider, useAutoIdentify } from '@fingerprint79/react-native';
export default function App() {
return (
<FpProvider
config={{
collectorUrl: 'https://yoursite.com/fpjs', // Cloudflare Worker route
projectKey: 'pk_live_xxx', // from the dashboard's API Keys page
}}
>
<Root />
</FpProvider>
);
}
function Root() {
const { data, loading, error } = useAutoIdentify({ event: 'pageview' });
if (loading) return <Text>Identifying…</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return <Text>Visitor: {data?.visitorId ?? 'pending'}</Text>;
}For per-action events (login, signup, checkout) use the imperative hook:
import { useIdentify } from '@fingerprint79/react-native';
function SignInButton() {
const { identify, loading } = useIdentify({ event: 'login', sync: true });
return (
<Button
title="Sign in"
disabled={loading}
onPress={async () => {
const r = await identify({ userId: 'u_123' });
// r.visitorId, r.suspectScore — ready synchronously when sync: true.
}}
/>
);
}Signals collected
Sent on every identify(), all fields optional at the wire level — missing data never breaks the request.
JS-side (RN APIs)
| Field | Source |
| ----------------------------- | -------------------------------------------------- |
| hardware.userAgent | Platform.OS + Platform.Version |
| hardware.platform | Platform.OS |
| hardware.screen | Dimensions.get('screen') × PixelRatio |
| intl.timezone | react-native-localize (or Intl.DateTimeFormat) |
| intl.language / languages | react-native-localize |
| network.effectiveType | @react-native-community/netinfo |
Native bridge
| Field | iOS source | Android source |
| ------------------------- | ------------------------------------------- | --------------------------------------- |
| mobile.os / osVersion | UIDevice.systemVersion | Build.VERSION.RELEASE |
| mobile.model | sysctlbyname("hw.machine") (iPhone15,2) | Build.MODEL |
| mobile.manufacturer | "Apple" | Build.MANUFACTURER |
| mobile.idfv | UIDevice.identifierForVendor | — |
| mobile.androidId | — | Settings.Secure.ANDROID_ID |
| mobile.installId | Keychain UUID, AfterFirstUnlock | EncryptedSharedPreferences UUID |
| mobile.totalMemoryMb | ProcessInfo.physicalMemory | ActivityManager.MemoryInfo |
| mobile.isTablet | userInterfaceIdiom == .pad | Configuration.SCREENLAYOUT_SIZE_LARGE |
| mobile.bundleId | Bundle.bundleIdentifier | Context.packageName |
| mobile.appVersion | CFBundleShortVersionString | PackageInfo.versionName |
What we do not collect
- IDFA / AppTrackingTransparency (would force an ATT prompt + Info.plist string)
- IMEI / phone number / Google Advertising ID (requires runtime permissions; Play Store policy review)
- Microphone, camera, contacts, location
The Keychain / EncryptedSharedPreferences installId plus IDFV / ANDROID_ID give us strong cross-launch visitor continuity without any of the above.
API
// Provider
<FpProvider config={{ collectorUrl, projectKey, serverPublicKeyB64?, sdkVersion? }}>
// Status
const { ready, error } = useFpStatus();
// Imperative
const { identify, data, loading, error } = useIdentify(defaults?);
// Automatic on mount
const { data, loading, error } = useAutoIdentify(opts?);IdentifyOptions:
| Field | Default | Effect |
| -------- | ------------ | ---------------------------------------------------------------------------------------------- |
| event | 'pageview' | One of pageview / login / signup / action / heartbeat. |
| userId | — | Surfaces as «Linked ID» in the dashboard. |
| sync | true | Wait for scoring (~50–150ms) so the response includes the real visitorId and suspectScore. |
License
MIT
