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
}
}
Customizing device profile values
-
The
DeviceCollector
protocol is a baseline class implementation protocol for theFRDeviceCollector
.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 -
Add the custom collector in the
DeviceProfileCallback
array ofProfileCollectors
:deviceProfileCallback.profileCollector.collectors.append(CustomCollector())
swift -
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
|
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:
-
To manually obtain device profile information, add the following code after the SDK initialization:
FRDevice.currentDevice?.getProfile(completion: { (deviceProfile) in print(deviceProfile) })
swiftWhen 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 |
---|---|
|
Main collector that includes other collectors and provides a collector version. |
|
Collect BLE support information of the device. |
|
Collect browser information from the device; specifically, the |
|
Collect camera information of the device. |
|
Collect display information of the device. |
|
Collect hardware-related information such as the number of CPUs, number of active CPUs, and so on. |
|
Collect location information of the device. |
|
Collect network information of the device. |
|
Collect platform-related information, such as device jailbreak status, time zone/locale, OS version, device name, and device model. |
|
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
}
}
Device profile attributes
The Ping SDK collects various device attributes, including the following:
Attribute | Value | |||
---|---|---|---|---|
|
A unique ID for the device. To learn more about the device identifier, refer to Uniquely identifying devices. |
|||
|
Metadata for the device, such as |
|||
|
Information about the operating system on the device, including:
|
|||
|
Information about the device such as |
|||
|
Information about the browser available on the device, including the |
|||
|
Information about the mobile service on the device, such as the |
|||
|
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]
}
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]
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
}
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()
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()