On this page

Implementation guide

Integrate without changing your transport

The SDK collects locally. Your application decides when to collect and how to send the event through its existing authenticated API.

Requirements

React Native0.76 or newer, New Architecture enabled
AndroidAPI 24 or newer
iOSCocoaPods and the minimum iOS version supported by the host React Native release
ExpoNative prebuild or custom development build; Expo Go is not supported

Install

npm install react-native-device-risk-signals
npx pod-install

Use the repository package manager and existing Bundler workflow when applicable. Autolinking registers the native module.

Expo prebuild and development builds

Use this package only in an Expo project that already uses native prebuild or a custom development client. Expo Go cannot load the TurboModule.

  1. Confirm the project uses React Native 0.76 or newer and does not disable the New Architecture.
  2. Install the package with Expo's version-aware installer:
npx expo install react-native-device-risk-signals
  1. After adding or upgrading the package, run npx expo prebuild --clean when android/ and ios/ are generated with Continuous Native Generation. A clean prebuild is the safest way to synchronize native code and is generally recommended for CNG-managed directories. It deletes and recreates android/ and ios/, so preserve manual changes first and do not use it over manually maintained native projects.
  2. If the repository keeps an existing ios/ project and does not regenerate it, install CocoaPods with its established command, such as npx pod-install or bundle exec pod install. A normal iOS prebuild installs pods unless it is run with --no-install.
  3. Create and install a new native binary with npx expo run:android, npx expo run:ios, or the project's existing EAS development-build profile. Metro reloads and over-the-air updates cannot add this native module to an already-built client.

After the native module is present, JavaScript-only application changes do not require another prebuild. Regenerate and rebuild when this package, Expo, React Native, or other native configuration changes.

No config plugin, additional permission, vendor service, or unrelated dependency is required for the base integration. Optional probes still depend on access the host application already declares or receives; review the permission matrix before enabling them.

Configure purpose and consent

Start with the smallest set that serves a documented purpose. Consent is subtractive: it can remove probes from collection, not silently enable additional ones.

import { consentFor, DeviceIntel } from "react-native-device-risk-signals";

const deviceIntel = new DeviceIntel({
  sessionId: session.id,
  consent: consentFor([
    "device_identity",
    "application",
    "os_integrity",
    "device_security_posture",
    "runtime",
  ]),
  probes: {
    network: { enabled: false },
    geolocation: { enabled: false },
    transaction_safety: { enabled: false },
  },
});
Do not enable high-sensitivity probes because they are available. Document the purpose, lawful basis, retention, and access policy first.

Host permission matrix

The library manifest declares no permissions and the SDK never displays a permission prompt. It uses sensitive access only when the host application has already declared or received it.

Host accessProbe and fieldsBehavior when unavailable
ACCESS_NETWORK_STATEnetwork: active transports, validation, captive portal, DNS, Private DNS, MTU, and link estimatesPermission-dependent fields are omitted. No network request is made.
READ_PHONE_STATE already grantedtelephony.simCountThe field is omitted. The SDK never requests phone-state permission.
Location permission already grantedgeolocation: cached coordinates, accuracy, provider, age, and mock-source stateCached-location fields are omitted. The SDK never requests location permission or starts active updates.
BLUETOOTH_CONNECT already grantedmedia_bluetooth_apps.bluetoothBondedDeviceCountThe count is omitted. No Bluetooth permission prompt is displayed.
DETECT_SCREEN_CAPTURE on Android 14+transaction_safety: screenshot observation active, detected state, and elapsed timeScreenshot fields are omitted. Android shows its standard notice only after an observed screenshot.
DETECT_SCREEN_RECORDING on Android 15+transaction_safety.isVisibleInScreenRecording and Android isScreenCaptured aliasRecording-visibility fields are omitted.

Add only the permissions required by the fields your application has approved. See the typed catalog for probe-level notes and the privacy guide before production collection.

Collect at a meaningful event

Attach a server-generated session or action identifier. Avoid stable identifiers created solely for fingerprinting.

const event = await deviceIntel.collect({
  sessionId: checkoutSessionId,
});

await api.post("/mobile-risk-observations", {
  accountActionId,
  observations: event,
});

The example endpoint is owned by the host application. It is not part of this package.

Collect protected-action context separately

transaction_safety is disabled by default. On Android, the first enabled collection starts lazy screenshot and obscured-touch observation. Collect once when the protected UI opens, then collect again immediately before its action.

const transactionConfig = {
  probes: {
    transaction_safety: { enabled: true, timeoutMs: 900 },
  },
};

await deviceIntel.collect({ config: transactionConfig });
// Later, immediately before the protected action:
const protectedActionEvent = await deviceIntel.collect({
  config: transactionConfig,
});

Obscured-touch flags need no permission. Android 14 screenshot detection and Android 15 recording visibility require the host application to declare DETECT_SCREEN_CAPTURE or DETECT_SCREEN_RECORDING respectively. Add only the required install-time permission to the host application's android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
<uses-permission android:name="android.permission.DETECT_SCREEN_RECORDING" />

The library declares neither permission and never prompts. Android displays its standard notice when a screenshot is detected. Missing capture fields mean the API, host permission, callback, or observation was unavailable; they do not mean capture was observed off.

Accessibility, screen-sharing, and overlays can be legitimate. Use them as review context, not an automatic accusation.

Backend contract

  1. Authenticate the mobile request using your normal session controls.
  2. Validate event schema, SDK version, collection timestamp, and action binding.
  3. Preserve probe outcome semantics. Missing, skipped, timeout, and error are distinct.
  4. Compute derived features and policy on a trusted backend.
  5. Apply retention, access controls, monitoring, and deletion requirements.
Treat all client telemetry as attacker-influenced. Device observations add context but never replace server-side controls or platform attestation.