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 returns 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
}
}
Customize the device profile callback
-
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) } }
-
Add the custom collector in the
DeviceProfileCallback
array ofProfileCollectors
:deviceProfileCallback.profileCollector.collectors.append(CustomCollector())
-
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 } }
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
SDK 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 FRDevice.currentDevice?.getProfile()
method to return device profile:
-
In your app, add the following code after the SDK initialization:
FRDevice.currentDevice?.getProfile(completion: { (deviceProfile) in print(deviceProfile) })
-
When the above code is triggered, the app prints JSON text, including the device profile, in the console.
The device
identifier
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": "uiCToe3h2kdfYLOzvHpaloxsKVE=",
"version": "1.0",
"platform": {
"jailbreakScore": 0.0,
"systemInfo": {
"sysname": "Darwin",
"release": "18.6.0",
"version": "Darwin Kernel Version 18.6.0: Thu Apr 25 22:14:08 PDT 2019; root:xnu-4903.262.2~2/RELEASE_ARM64_T8015",
"nodename": "James-Go-iPhone-X",
"machine": "iPhone10,6"
},
"timezone": "America/Vancouver",
"model": "iPhone10,6",
"version": "12.3.1",
"locale": "en",
"platform": "iOS",
"device": "iPhone",
"brand": "Apple",
"deviceName": "James Go iPhone X"
},
"hardware": {
"cpu": 6,
"storage": 60975.11328125,
"manufacturer": "Apple",
"activeCPU": 6,
"display": {
"orientation": 1,
"width": 375.0,
"height": 812.0
},
"memory": 2823.0,
"multitaskSupport": true,
"camera": {
"numberOfCameras": 4
}
},
"bluetooth": {
"supported": true
},
"browser": {
"agent": "FRExample/1.0 (com.forgerock.frexample; iPhone; build:1; iOS 12.3.1) CFNetwork/978.0.7 Darwin/18.6.0 FRAuth/1.0"
},
"telephony": {
"mobileCountryCode": "302",
"voipEnabled": true,
"isoCountryCode": "ca",
"mobileNetworkCode": "220",
"carrierName": "TELUS"
},
"network": {
"connected": true
},
"location": {
"latitute": 37.7873589,
"longitude": -122.408227
}
}
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]
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)
}
}
}
Device profile attributes
By default, the Ping SDK collects the following device attributes:
Attribute | Value |
---|---|
|
A unique ID for the device. To learn more about the device identifier, refer to Uniquely identifying devices. |
|
The location of a device (longitude and latitude values). This is configured in the node and requires user permissions. |
|
Metadata for the device, including:
|
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()
Known limitations
For ForgeRock SDK for iOS v2.2.0 and earlier, the iOS SDK has discrepancies in attribute names inside the JSON payload. ForgeRock SDK for iOS v3.0.0 and later correct the attribute names to align with PingAM’s expectation and other platforms. The following attribute names were changed as of 3.0.0:
Attribute Names for 2.2.0 and earlier | Attribute Names for 3.0.0 and later |
---|---|
|
|
|
|
If your application still uses iOS SDK 2.2.0 or older,
an adjustment in the Custom Matching Script is required in the Device Match Node
to collect and analyze correct attribute values from the SDK.
|