Ping SDKs

Set up device profiling in iOS apps

This page shows how to detect the DeviceProfileCallback, how to collect the device profile, and how to send the profile to PingAM.

PingAM includes collected device information in its audit logs by default.

To configure PingAM to filter out this information and ensure no personally identifiable information (PII) is written to the audit logs, refer to Prevent auditing of device data.

Handle a device profile callback

If an authentication journey uses the device profile node, the SDK receives a DeviceProfileCallback to collect device attributes.

You use various SDK methods to handle the callback.

Use the default device profile callback

deviceProfileCallback.execute { _ in node.next
  { (user: FRUser?, node, error) in
   self.handleNode(user: user, node: node, error: error) //Handle the node
  }
}
swift

Customizing device profile values

  1. The DeviceCollector protocol is a baseline class implementation protocol for the FRDeviceCollector.

    Create a new class inheriting the protocol and implement the protocol methods:

    public class CustomCollector: DeviceCollector {
          public var name: String = "customCollector"
          public func collect(completion: @escaping DeviceCollectorCallback) {
            var result: [String: Any] = [:]
            result["customID"] = "MyCustomIDValue"
            completion(result)
          }
     }
    swift
  2. Add the custom collector in the DeviceProfileCallback array of ProfileCollectors:

    deviceProfileCallback.profileCollector.collectors.append(CustomCollector())
    swift
  3. To collect the device profile and submit the node, use the DeviceProfileCallback.execute() method:

    deviceProfileCallback.execute { _ in node.next
      { (user: FRUser?, node, error) in
        self.handleNode(user: user, node: node, error: error) // Handle the node
      }
    }
    swift

If you want to remove any of the default profile collectors, access the profile collectors array of the DeviceProfileCallback node. The default list contains the following collectors:

  • PlatformCollector()

  • HardwareCollector()

  • BrowserCollector()

  • TelephonyCollector()

  • NetworkCollector()

Manually collect device profile information

Instead of responding to a device callback, your app can get the device profile using default collectors.

The FRAuth module provides a device profile feature that enables you to identify and obtain details about the device.

The FRDevice class includes methods for getting information about a device. Use the FRDeviceCollector.shared.collect method to return device profile:

  1. To manually obtain device profile information, add the following code after the SDK initialization:

    FRDevice.currentDevice?.getProfile(completion: { (deviceProfile) in
        print(deviceProfile)
    })
    swift

    When the above code is triggered, the app outputs the device profile to the Xcode console.

    The identifier displayed in the device profile is not the same as Apple’s device vendor identifier.

    As long as the Keychain Service configuration remains unchanged, the device identifier should remain the same for this device, even if you delete and reinstall the application.

Default collectors

Collector name Description

FRDeviceCollector

Main collector that includes other collectors and provides a collector version.

BluetoothCollector

Collect BLE support information of the device.

BrowserCollector

Collect browser information from the device; specifically, the User-Agent.

CameraCollector

Collect camera information of the device.

DisplayCollector

Collect display information of the device.

HardwareCollector

Collect hardware-related information such as the number of CPUs, number of active CPUs, and so on.

LocationCollector

Collect location information of the device.

NetworkCollector

Collect network information of the device.

PlatformCollector

Collect platform-related information, such as device jailbreak status, time zone/locale, OS version, device name, and device model.

TelephonyCollector

Collect telephony information of the device.

Sample device profile

{
  "identifier": "18cf87d53e45890598bdd392d13a640903447ab2",
  "metadata": {
    "bluetooth": {
      "supported": false
    },
    "network": {
      "connected": true
    },
    "browser": {
      "userAgent": "FRAuth/4.8.1 (com.forgerock.ios.FRAuth; iPhone; build:1; iOS 18.2.0) CFNetwork/1.0 Darwin/24.6.0"
    },
    "hardware": {
      "manufacturer": "Apple",
      "memory": 32768,
      "cpu": 10,
      "display": {
        "height": 932,
        "width": 430,
        "orientation": 0
      },
      "camera": {
        "numberOfCameras": 0
      }
    },
    "platform": {
      "version": "18.2",
      "timeZone": "Europe/London",
      "platform": "iOS",
      "jailBreakScore": 0,
      "brand": "Apple",
      "locale": "en",
      "deviceName": "iPhone 16 Plus",
      "model": "arm64",
      "device": "iPhone"
    },
    "telephony": {
      "carrierName": "Unknown",
      "networkCountryIso": "Unknown"
    }
  },
  "version": "1.0",
  "location": {
    "latitude": 37.7873589,
    "longitude": -122.408227
  }
}
json

Device profile attributes

The Ping SDK collects various device attributes, including the following:

Attribute Value

identifier

A unique ID for the device.

To learn more about the device identifier, refer to Uniquely identifying devices.

metadata

Metadata for the device, such as platform, hardware, browser, and telephony attributes.

platform

Information about the operating system on the device, including:

platform

The device OS.

deviceName

The name of the device.

locale

The locale of the device, such as en.

timeZone

The time zone of the device, such as Africa/Johannesburg.

brand

The brand of the device, such as Apple.

jailBreakScore

A value between 0.0 and 1.0 that denotes the tampering level for a device. A higher value indicates a higher probability that the device has been tampered with.

Running on an iOS device simulator always returns 1.0.

hardware

Information about the device such as cpu, display, and storage.

browser

Information about the browser available on the device, including the agent property.

telephony

Information about the mobile service on the device, such as the carrier.

location

The location of a device (longitude and latitude values).

This is configured in the node and requires user permissions.

Modify the default collectors

You can collect the device profile using default collectors. You can also modify the default collectors.

The following code collects the device profile using the default collectors:

FRDeviceCollector.shared.collect { (result) in
    // Result is returned as [String: Any]
}
swift

The collectors array in the FRDeviceCollector class is a public property. You can add, remove, or change any of the collectors:

@objc
  public var collectors: [DeviceCollector]
swift

Create a custom collector

class CustomCollector: DeviceCollector {
    var name: String = "custom"

    func collect(completion: @escaping DeviceCollectorCallback) {
        var result: [String: Any] = [:]

        // Perform logic to collect any device profile
        result["key"] = "value"

        completion(result)
    }
}
swift

Obtain user permission for the device location

Your app requires the user’s authorization to access the device location.

For information about how to request the authorization, refer to Requesting Authorization for Location Services.

Implement default jailbreak/rooted device detection

The FRJailbreakDetector class is responsible for analyzing whether the device is tampered.

The class analyzes the device by using multiple device tamper detectors and returns the highest score in the range between 0.0 to 1.0 from all the detectors.

On iOS, a device simulator always returns 1.0.

Sample using default tamper detection:

// Check if device is jailbroken
let jailbrokenScore = FRJailbreakDetector.shared.analyze()

// Evaluate the result
if jailbrokenScore == -1.0 {
    // no detectors found
}
else if jailbrokenScore == 0.0 {
    // Means that the detectors score result is 0.0
}
else {
    // Some detectors returned a possible positive result that indicates the device might be jailbroken
}
swift

Customize jailbreak/rooted detection

The SDKs provide a set of well-known detectors. You can choose the detectors to use.

Sample custom tamper detection code:

// Remove everything and only add your selected detectors
FRJailbreakDetector.shared.detectors.removeAll()
FRJailbreakDetector.shared.detectors.append(BashDetector())
FRJailbreakDetector.shared.detectors.append(SSHDetector())

// Or loop through detectors and remove specific detector from default
for detector in FRJailbreakDetector.shared.detectors {
    // Remove specific detector if required
}

// Get result
let jailbrokenScore = FRJailbreakDetector.shared.analyze()
swift

Implement custom detectors

Developers can implement their own detectors by extending the JailbreakDetector protocol on iOS. This protocol represents the definition of a individual analyzer for detecting if a device is rooted or jailbroken.

Each detector should analyze its logic to determine whether the device is rooted or jailbroken. Each detector returns the result score as a Double, within the range of 0.0 to 1.0.

Sample custom detector code:

// Custom detector
class CustomDetector: JailbreakDetector {
    public func analyze() -> Double {
        var result = 0.0
        // do the custom logic
        return result
    }
}

// Add custom detector to JailbreakDetector
FRJailbreakDetector.shared.detectors.append(CustomDetector())

// Get the result
let jailbrokenScore = FRJailbreakDetector.shared.analyze()
swift

More information