Device self-service in React Native apps
PingOne Advanced Identity Cloud PingAM React Native
The Device Client module for React Native 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 React Native applications.
Installing modules
To install the module into your React Native project, use yarn or npm as follows:
-
yarn
-
npm
yarn add @ping-identity/rn-device-client
npm install @ping-identity/rn-device-client
After installation, import the module as follows:
import { createDeviceClient } from '@ping-identity/rn-device-client';
Initializing the device client
The Device Client module uses the REST-based device management endpoints provided by Advanced Identity Cloud and PingAM.
The module requires a session token when making calls to the device management endpoints. In a React Native app, you must obtain the SSO token explicitly and pass it to the device client.
Session tokens often have a short duration and may expire after 5 minutes. If the client does not have an active session token, trigger an authentication journey to obtain a new one before attempting to manage registered devices.
Call createDeviceClient() after authenticating a user and obtaining their session token:
import { useJourney } from '@ping-identity/rn-journey';
import { createDeviceClient } from '@ping-identity/rn-device-client';
import type { JourneyClient } from '@ping-identity/rn-journey';
const [, journeyActions] = useJourney(journeyClient);
async function loadDevices() {
const token = await journeyActions.ssoToken();
if (!token) {
// No active session — redirect to login
return;
}
const deviceClient = createDeviceClient({
serverUrl: 'https://openam-forgerock-sdks.forgeblocks.com/am',
ssoToken: token.value,
realm: 'alpha',
cookieName: 'ch15fefc5407912',
});
try {
const devices = await deviceClient.oath.get();
// use devices
} catch (error) {
// Token may have expired. Re-authenticate and retry
console.error('Failed to fetch devices:', error);
} finally {
await deviceClient.dispose();
}
}
The configuration properties for the device client are as follows:
| Parameter | Description | Required? |
|---|---|---|
|
The base URL of your server, including the access management path.
|
Yes |
|
The SSO token for the authenticated user. Obtain this by calling Learn more in Managing sessions and tokens in React Native. |
Yes |
|
The authentication realm.
|
Yes |
|
The name of the SSO cookie configured on the server. For example, PingAM servers use How do I find my PingOne Advanced Identity Cloud cookie name?To locate the cookie name in an PingOne Advanced Identity Cloud tenant:
|
Yes |
|
Optional logger instance created by Learn more in Configuring logging in React Native. |
No |
Listing registered devices
For each type of registered device, call the get() method to retrieve the list from the server.
The available device types are as follows:
| Registered device type | Listing method |
|---|---|
WebAuthn / FIDO2 authenticators |
|
Bound devices |
|
Device profiles |
|
Push MFA devices |
|
OATH MFA devices (TOTP / HOTP) |
|
Each method returns a Promise. Different device types return different information you can display to the user, such as the device name or last access date.
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
const devices = await deviceClient.webAuthn.get();
devices.forEach((device) => {
console.log('id:', device.id);
console.log('deviceName:', device.deviceName);
console.log('uuid:', device.uuid);
console.log('createdDate:', new Date(device.createdDate).toISOString());
console.log('lastAccessDate:', new Date(device.lastAccessDate).toISOString());
console.log('credentialId:', device.credentialId);
console.log('---');
});
const devices = await deviceClient.bound.get();
devices.forEach((device) => {
console.log('id: ', device.id);
console.log('deviceName: ', device.deviceName);
console.log('uuid: ', device.uuid);
console.log('deviceId: ', device.deviceId);
console.log('createdDate: ', new Date(device.createdDate).toISOString());
console.log('lastAccessDate:', new Date(device.lastAccessDate).toISOString());
console.log('---');
});
const devices = await deviceClient.profile.get();
devices.forEach((device) => {
console.log('id: ', device.id);
console.log('deviceName: ', device.deviceName);
console.log('identifier: ', device.identifier);
console.log('metadata: ', JSON.stringify(device.metadata, null, 2));
console.log('location: ', device.location
? `lat ${device.location.latitude}, lon ${device.location.longitude}`
: 'none');
console.log('lastSelectedDate: ', new Date(device.lastSelectedDate).toISOString());
console.log('---');
});
const devices = await deviceClient.push.get();
devices.forEach((device) => {
console.log('id: ', device.id);
console.log('deviceName: ', device.deviceName);
console.log('uuid: ', device.uuid);
console.log('createdDate: ', new Date(device.createdDate).toISOString());
console.log('lastAccessDate:', new Date(device.lastAccessDate).toISOString());
console.log('---');
});
const devices = await deviceClient.oath.get();
devices.forEach((device) => {
console.log('id: ', device.id);
console.log('deviceName: ', device.deviceName);
console.log('uuid: ', device.uuid);
console.log('createdDate: ', new Date(device.createdDate).toISOString());
console.log('lastAccessDate:', new Date(device.lastAccessDate).toISOString());
console.log('---');
});
Renaming devices
Use the update() method to rename a registered device and save the change to the server.
Obtain the device object from get(), set the new deviceName, and pass it to update(). update() returns a Promise that resolves with the updated device object.
-
Bound devices
-
OATH devices
const devices = await deviceClient.bound.get();
const device = devices[0];
const updatedDevice = await deviceClient.bound.update({
...device,
deviceName: 'My Updated Device Name',
});
console.log('Updated bound device:', updatedDevice);
const devices = await deviceClient.oath.get();
const device = devices[0];
const updatedDevice = await deviceClient.oath.update({
...device,
deviceName: 'Work Authenticator',
});
console.log('Updated OATH device:', updatedDevice);
Deleting devices
Use the delete() method to delete a device from the user’s profile.
The authentication journey that provided the user’s session must meet one or more of the following criteria:
-
Used the 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 have authenticated using a WebAuthn device.
-
Used the Enable Device Management node that alters the Device Check Enforcement Strategy.
Obtain the device object from get(), and then pass it to delete(). The delete() function returns a Promise that resolves with the deleted device object.
-
Push devices
-
WebAuthn devices
const devices = await deviceClient.push.get();
const device = devices[0];
await deviceClient.push.delete(device);
console.log('Push device deleted');
const devices = await deviceClient.webAuthn.get();
const device = devices[0];
await deviceClient.webAuthn.delete(device);
console.log('WebAuthn device deleted');
Handling errors
All device client operations throw a DeviceClientError when they fail.
DeviceClientError extends the base PingError class and exposes a code property you can use to identify the cause and take appropriate action, such as redirecting to login when a token expires.
The following code shows how to handle errors from a device operation:
import { DeviceClientError } from '@ping-identity/rn-device-client';
try {
const devices = await deviceClient.oath.get();
} catch (error) {
if (error instanceof DeviceClientError) {
switch (error.code) {
case 'DEVICE_CLIENT_INVALID_TOKEN':
// SSO token is missing, expired, or rejected — re-authenticate
break;
case 'DEVICE_CLIENT_NETWORK_ERROR':
// Network connectivity failure
break;
case 'DEVICE_CLIENT_NOT_FOUND':
// The requested device does not exist on the server
break;
default:
console.error('Device operation failed:', error.message);
}
}
}
The module returns the following error codes:
| Error code | Description |
|---|---|
|
Generic or unknown error. |
|
Network connectivity failure. |
|
The server returned a non-success HTTP status. |
|
The SSO token is missing, expired, or rejected by the server. Obtain a fresh token by triggering a new authentication journey. |
|
The native bridge could not parse the server response. |
|
Required configuration fields were not provided to |
|
The requested device does not exist on the server. |
|
The client has already been disposed. Call |