Device self-service in Android apps
PingOne Advanced Identity Cloud PingAM Android
The Device Client module for Android provides a comprehensive and unified API for managing Multi-Factor Authentication (MFA) devices and user profile devices registered with Advanced Identity Cloud or PingAM servers.
The module simplifies the process of retrieving, updating, and deleting various types of authentication devices, enabling you to build secure and user-friendly device management experiences within your Android applications.
Installing modules
To install the Device Client module into your Android app:
-
In the Project tree view of your Android Studio project, open the
build.gradle.ktsfile. -
In the
dependenciessection, add thedevice-clientmodule as a dependency:dependencies { implementation("com.pingidentity.sdks:journey:2.0.0") implementation("com.pingidentity.sdks:device-client:2.0.0") }
Initializing the device client
The Device Client module uses the REST-based device management endpoints provided by Advanced Identity Cloud and PingAM.
The Device Client module requires the session token when making calls to the device management endpoints.
Session tokens often have a short duration and might expire after 5 minutes. If the client does not have an active session token you should trigger an authentication journey to obtain a new session token before attempting to manage registered devices.
The following code shows how to initialize the device client module after authenticating a user and obtaining their session token:
import com.pingidentity.journey.Journey
import com.pingidentity.device.client.DeviceClient
import java.net.URL
val journey = Journey {
serverUrl = URL("https://openam-forgerock-sdks.forgeblocks.com/am")
realm = "alpha"
}
// Authenticate the user
var node = journey.start("sdkUsernamePasswordJourney")
// ... Step through the journey nodes until success...
if (node is SuccessNode) {
val session = node.session
val ssoToken = session.value // Extract the SSO token
// Create DeviceClient with the SSO token
val deviceClient = DeviceClient {
ssoTokenString = ssoToken
serverUrl = URL("https://openam-forgerock-sdks.forgeblocks.com/am")
realm = "alpha"
cookieName = "ch15fefc5407912"
}
}
The configuration properties for the device client are as follows:
| Parameter | Description | Required? |
|---|---|---|
|
The session, or SSO token, obtained after successful authentication. |
Yes |
|
The base URL of your server.
|
Yes |
|
The authentication realm.
|
Yes |
|
The session cookie name.
|
Yes |
Listing registered devices
For each type of registered device you can call the devices() method to retrieve a list from the server.
The available types of device are as follows:
| Registered device type | Listing method |
|---|---|
WebAuthn / FIDO authenticators |
|
Bound devices |
|
Profiled devices |
|
Push MFA devices |
|
OATH MFA devices |
|
Each method returns a Result type for improved error handling and functional programming support.
The different device types return different relevant information that you can access and display to the user as appropriate, such as the current name or when the device was last used.
The following code shows how to obtain lists of the different devices from the server:
-
WebAuthn / FIDO
-
Bound devices
-
Profiled devices
-
Push devices
-
OATH devices
deviceClient.webAuthnDevice.devices().onSuccess { webAuthnDevices ->
webAuthnDevices.forEach { device ->
println("Device ID: ${device.id}")
println("Device Name: ${device.deviceName}")
println("Credential ID: ${device.credentialId}")
println("---")
}
}.onFailure { exception ->
println("Error fetching WebAuthn devices: ${exception.message}")
}
deviceClient.boundDevice.devices().onSuccess { boundDevices ->
boundDevices.forEach { device ->
println("Device ID: ${device.id}")
println("Device Name: ${device.deviceName}")
println("Device ID: ${device.deviceId}")
println("UUID: ${device.uuid}")
println("---")
}
}.onFailure { exception ->
println("Error fetching Bound devices: ${exception.message}")
}
deviceClient.profileDevice.devices().onSuccess { profileDevices ->
profileDevices.forEach { device ->
println("Device ID: ${device.id}")
println("Device Name (Alias): ${device.deviceName}")
println("Identifier: ${device.identifier}")
println("Metadata: ${device.metadata}")
device.location?.let { location ->
println("Location: ${location.latitude}, ${location.longitude}")
}
println("Last Selected: ${device.lastSelectedDate}")
println("---")
}
}.onFailure { exception ->
println("Error fetching Profile devices: ${exception.message}")
}
deviceClient.pushDevice.devices().onSuccess { pushDevices →
pushDevices.forEach { device ->
println("Device ID: ${device.id}")
println("Device Name: ${device.deviceName}")
println("UUID: ${device.uuid}")
println("---")
}
}.onFailure { exception ->
println("Error fetching Push devices: ${exception.message}")
}
deviceClient.oathDevice.devices().onSuccess { oathDevices ->
oathDevices.forEach { device ->
println("Device ID: ${device.id}")
println("Device Name: ${device.deviceName}")
println("UUID: ${device.uuid}")
println("Created: ${device.createdDate}")
println("Last Access: ${device.lastAccessDate}")
println("---")
}
}.onFailure { exception ->
println("Error fetching OATH devices: ${exception.message}")
}
Renaming devices
You can rename registered devices and update the user’s account with the new details by using the update() method.
For example, the following code renames a registered bound device:
deviceClient.boundDevice.devices().onSuccess { devices ->
if (devices.isNotEmpty()) {
val device = devices.first()
// Update the device name
device.deviceName = "My Pixel 10 Pro Fold"
deviceClient.boundDevice.update(device).onSuccess {
println("Device updated successfully!")
}.onFailure { exception ->
println("Failed to update device: ${exception.message}")
}
}
}
|
All update operations automatically include an This header is used for optimistic concurrency control to prevent conflicts when multiple clients attempt to modify the same device simultaneously. The wildcard |
Deleting devices
You can delete or deregister a device, with the caveat that the authentication journey that provided the users' session must fulfil one or more of the following criteria:
-
Used same multi-factor authentication method as the device you want to delete.
For example, to delete a WebAuthn device the authentication journey that created the session must also authenticate using a WebAuthn device.
-
Used the Enable Device Management node that alters the Device Check Enforcement Strategy.
Use the delete() method to delete a device from the user’s profile:
deviceClient.pushDevice.devices().onSuccess { devices ->
if (devices.isNotEmpty()) {
val deviceToDelete = devices.first()
deviceClient.pushDevice.delete(deviceToDelete).onSuccess {
println("Device deleted successfully!")
}.onFailure { exception ->
println("Failed to delete device: ${exception.message}")
}
}
}