Orchestration SDKs

Collecting device profiles in React Native apps

PingOne Advanced Identity Cloud PingAM React Native

This page guides you through collecting device profile data in a React Native application using @ping-identity/rn-device-profile.

It includes preconfigured collectors to collect attributes, and allows you to select which collectors to use to suit your requirements.

Step 1. Installing the module

To install the module into your React Native project, use yarn or npm as follows:

  • yarn

  • npm

yarn add @ping-identity/rn-device-profile
npm install @ping-identity/rn-device-profile

For Journey-integrated collection, also install the Journey module if you have not already done so:

  • yarn

  • npm

yarn add @ping-identity/rn-journey
npm install @ping-identity/rn-journey

After installation, import the functions you need:

Importing device profile functions
import {
  collectDeviceProfile,
  collectDeviceProfileForJourney,
} from '@ping-identity/rn-device-profile';
import type { DeviceProfileCollector } from '@ping-identity/rn-device-profile';

Native module setup

@ping-identity/rn-device-profile is a TurboModule package. On React Native 0.80.1+ with the new architecture enabled, linking is automatic. On older projects using the legacy bridge, run:

npx pod-install     # iOS

No additional android/ configuration is required.

Step 2. Declaring permissions

Some collectors require platform permissions. Add the necessary declarations to your project before enabling those collectors.

iOS

Open ios/<AppName>/Info.plist as source code and add the following entries as children of the top-level <dict> element:

Adding iOS permission entries to Info.plist
<!-- Required for LocationCollector -->
<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to enhance security and provide personalised experiences.</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>This app needs location access to enhance security and provide personalised experiences.</string>

<!-- Required for BluetoothCollector -->
<key>NSBluetoothAlwaysUsageDescription</key>
<string>This app uses Bluetooth to collect device information.</string>

Android

Open android/app/src/main/AndroidManifest.xml and add the following <uses-permission> entries inside <manifest>:

Adding Android permission entries to AndroidManifest.xml
<!-- Required for LocationCollector -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- Required for BluetoothCollector (Android 12+) -->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN"
    android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- For Android 11 and earlier -->
<uses-permission android:name="android.permission.BLUETOOTH" />

For LocationCollector and BluetoothCollector, you must also request the runtime permission in JavaScript before calling the collector. The SDK does not request permissions automatically on Android. Use React Native’s PermissionsAndroid API or a library such as react-native-permissions to request them at an appropriate point in your UX flow.

On iOS, LocationCollector requests the permission automatically when the data is first gathered.

Step 3. Collecting device profiles

Call collectDeviceProfile() with an array of collector names to gather device attributes:

Collecting a device profile with selected collectors
import { collectDeviceProfile } from '@ping-identity/rn-device-profile';
import { logger } from '@ping-identity/rn-logger';

const profileLogger = logger({ level: 'debug' });

const profile = await collectDeviceProfile(
['platform', 'hardware', 'network'],
// Add optional logger
{ logger: profileLogger },
);

// Output collected profile JSON
console.log(JSON.stringify(profile, null, 2));

The function returns a plain JavaScript object containing the merged output of all requested collectors. Collectors that fail are omitted from the result without throwing an error.

Available collectors

Collector value Collector output

'platform'

{
  "platform": {
    "platform": "iOS",
    "version": "17.0.1",
    "device": "iPhone",
    "deviceName": "Jane's iPhone",
    "model": "iPhone15,2",
    "brand": "Apple",
    "locale": "en",
    "timeZone": "America/New_York",
    "jailBreakScore": 0.0
  }
}

'hardware'

{
  "hardware": {
    "manufacturer": "Apple",
    "memory": 6144,
    "cpu": 6,
    "display": {
      "width": 393,
      "height": 852,
      "orientation": 1
    },
    "camera": {
      "numberOfCameras": 3
    }
  }
}

'network'

{
  "network": {
    "connected": true
  }
}

'telephony'

{
  "telephony": {
    "networkCountryIso": "US",
    "carrierName": "Verizon"
  }
}

'browser'

{
  "browser": {
    "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15..."
  }
}

'bluetooth'

{
  "bluetooth": {
    "supported": true
  }
}

'location'

{
  "location": {
    "latitude": 37.2431,
    "longitude": 115.7930
  }
}

For most applications, platform, hardware, and network provide a solid baseline signal without requiring any runtime permissions:

const profile = await collectDeviceProfile(['platform', 'hardware', 'network']);

Step 4. Integrating with Advanced Identity Cloud and PingAM journeys

When a journey reaches a Device Profile Collector node, the SDK receives a DeviceProfileCallback.

Call collectDeviceProfileForJourney() to collect the device signals and write them into the active journey callback, then call actions.next() to advance the node. DeviceProfileJourneyResult is an imported type that represents the resolved value from a device profile collection step inside a journey:

Handling a DeviceProfileCallback in a journey node
import { collectDeviceProfileForJourney } from '@ping-identity/rn-device-profile';
import { collectDeviceProfileForJourney } from '@ping-identity/rn-device-profile';
import type { DeviceProfileJourneyResult } from '@ping-identity/rn-device-profile';
import { useJourney, useJourneyForm } from '@ping-identity/rn-journey';
import type { JourneyClient } from '@ping-identity/rn-journey';

function useDeviceProfileNode(journeyClient: JourneyClient) {
  const [node, actions] = useJourney(journeyClient);
  const form = useJourneyForm(node);

  const submit = async (): Promise<void> => {
    const hasDeviceProfile = form.fields.some(
      (f) => f.ref.type === 'DeviceProfileCallback',
    );

    if (!hasDeviceProfile) return;

    const result: DeviceProfileJourneyResult =
      await collectDeviceProfileForJourney(journeyClient, [
        'platform',
        'hardware',
        'network',
      ]);

    if (result.type === 'success') {
      await actions.next();
    }
  };

Call collectDeviceProfileForJourney once per node. It inspects the active node from the journey client automatically. You do not need to pass individual callback references or indexes.

The journey profile payload

The profile written to the journey callback includes a device identifier generated by the native platform, the collector output, and any location data. The server-side Device Profile Collector node uses the identifier field to correlate the submission with a known device:

Example journey profile payload
{
  "identifier": "a3f8d2...",
  "metadata": {
    "platform": {
      "platform": "iOS",
      "version": "17.0.1",
      "device": "iPhone"
    },
    "hardware": {
      "manufacturer": "Apple",
      "memory": 6144,
      "cpu": 6
    },
    "network": {
      "connected": true
    }
  },
  "location": {
    "latitude": 37.2431,
    "longitude": 115.7930
  }
}

For details on how the device identifier is generated and how to customize it, see Customizing device identifiers in React Native apps.