Step 6. Configure the Ping (ForgeRock) Authenticator module for push notifications
In this step, you add the code to your application that obtains the unique device token required to ensure push notifications reach their intended audience.
You also add code that leverages the Ping (ForgeRock) Authenticator module to handle the push registration and authentication journey you created earlier.
Prerequisites
To complete the procedures on this page, you must set up your application to use the Ping (ForgeRock) Authenticator module:
Register a device token to receive notifications
The Ping (ForgeRock) Authenticator module uses an Apple or Google service to receive push notifications sent from your server via Amazon SNS.
Each instance of your application requires a unique device registration token to receive these push notifications.
Use the registerForRemoteNotifications()
method to register the token:
-
Android
-
iOS
// Retrieve the FCM token
FirebaseMessaging.getInstance().getToken()
.addOnCompleteListener(task -> {
if (!task.isSuccessful()) {
Log.e(TAG, "getInstanceId failed", task.getException());
return;
}
// Get new Instance ID token
fcmToken = task.getResult();
Log.v("FCM token:", fcmToken);
// Register the token with the SDK to enable Push mechanisms
try {
fraClient.registerForRemoteNotifications(fcmToken);
} catch (AuthenticatorException e) {
Log.e(TAG,"Error registering FCM token: ", e);
}
});
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Register Push Notification for your app in AppDelegate.swift
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in }
application.registerForRemoteNotifications()
return true
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Upon successful registration and receiving device token from APNs, register the token to the Authenticator module
FRAPushHandler.shared.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// Upon receiving an error from APNs, notify Authenticator module to properly update the status
FRAPushHandler.shared.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
}
Updating device tokens for existing accounts
Under certain circumstances the client operating system issues a new device token that your app needs to use for receiving push notifications.
What can cause the device token for push messages to change?
The device token used to receive push messages can change due to a number of circumstances:
- Uninstalling and reinstalling the client app
-
If the user uninstalls and then reinstalls the app, the OS regenerates the device token.
This is one of the most common reasons for a token change.
- Clearing app data
-
Clearing the application’s data by using the device settings causes the OS to issue a new device token upon next launch of the app.
- Revoking and regranting Push permission
-
The OS might issue a new device token if a user revokes and then re-enables push notifications.
- Push services expiring or invalidating tokens
-
The push services themselves, such as the Apple Push Notification service (APNs) or Google’s Firebase Cloud Messaging (FCM) service might invalidate device tokens for various reasons.
The OS issues a new device token upon next launch of the app if the push service invalidates the existing tokens.
- Updating the operating system
-
Occasionally, OS updates, especially major versions, might result in the push notification service issuing a new token.
Updating the OS can also clear app data, which would also mean the app requires a new device token on next launch.
- Updating or migrating apps
-
If you change the package or bundle IDs of your client app that uses push notification, or alter the signing keys, the OS might invalidate existing device tokens and issue a new one.
Similarly, if the user restores the app from backup, or migrates the app to a different device, the OS might issue a new device token, even if restoring the app to the same physical device.
The Ping SDKs for Android and iOS provide methods for updating the device token associated with accounts it has registered to receive Push notifications. These methods also contact the PingAM server or PingOne Advanced Identity Cloud tenant that registered the device to update the user’s device token.
Failure to update the device token both within the app and on the server will prevent push messages arriving on the device, causing authentication failures.
Updating existing accounts with a new device token is only supported by the following server:
|
-
Android
-
iOS
Implement the onNewToken
method within a custom FirebaseMessagingService
subclass to receive the updated token.
The Ping SDK for Android provides the following methods to facilitate updating accounts and the server with the new device token:
FRAClient.updateDeviceToken()
-
This method updates the device token for all accounts registered to receive push notifications.
Updating all accounts registered for push notificationsfraClient.updateDeviceToken(token, new FRAListener<Void>() { @Override public void onSuccess(Void result) { //DeviceToken is updated successfully } @Override public void onException(Exception e) { //Failed to update DeviceToken } });
FRAClient.updateDeviceTokenForMechanism()
-
This method updates the device token for a single account registered to receive push notifications.
Updating a single account registered for push notificationsfraClient.updateDeviceTokenForMechanism(token, pushMechanism, new FRAListener<Void>() { @Override public void onSuccess(Void result) { //DeviceToken is updated successfully } @Override public void onException(Exception e) { //Failed to update DeviceToken } });
If iOS issues a new device token it triggers the didRegisterForRemoteNotificationsWithDeviceToken
application method.
The AppDelegate methods in the FRAPushHandler
class store issued device tokens locally, and automatically updates local registered accounts. They also contact your server to update the user profiles with the new device token.
FRAPushHandler.application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data)
The Ping SDK for iOS provides the following methods to manually update accounts and the server with the new device token:
FRAPushHandler.updateDeviceToken()
-
This method updates the device token for all accounts registered to receive push notifications.
Updating all accounts registered for push notificationsFRAPushHandler.instance.updateDeviceToken(deviceToken: deviceTokenString, onSuccess: { // DeviceTokens updated successfully }) { (error) in // Failed to update DeviceTokens with following error: \(error.localizedDescription) }
FRAPushHandler.updateDeviceToken()
-
This method updates the device token for a single account registered to receive push notifications.
Updating a single account registered for push notificationsFRAPushHandler.instance.updateDeviceToken(mechanism: pushMechanism, deviceToken: deviceTokenString, onSuccess: { // DeviceToken updated successfully }) { (error) in // Failed to update DeviceToken with following error: \(error.localizedDescription) }
Retrieving the existing device token
The Ping SDKs for Android and iOS automatically persist the device token, and provide functions for retrieving it. This could be useful in the following situations:
-
To confirm the stored token was correctly persisted by your app when updating a token provided by the OS.
-
To register the existing device token with external services other than Advanced Identity Cloud or AM.
-
To check if a valid token exists, which helps when debugging notification delivery issues.
To retrieve the existing device token used by the app, perform the following tasks:
- Android
-
Use the
FRAClient.getPushDeviceToken()
method to retrieve thePushDeviceToken
object, that contains the token and its issuance timestamp. If no token is available, it returnsnull
. - iOS
-
Use the
FRAPushHandler.deviceToken
property that returns the current device token, ornil
if it is not available.
Handle registration of the app for push notifications
The first time you authenticate to your authentication tree, you are asked to register a device by scanning a QR code.
Your application must implement a QR code scanning mechanism. The QR code contains the URI used for registering the device, although you could also offer a method for entering the URI manually. |
After capturing the URI, register the authentication mechanism in your app:
-
Android
-
iOS
fraClient.createMechanismFromUri("qrcode_scan_result", new FRAListener<Mechanism>() {
@Override
public void onSuccess(Mechanism mechanism) {
// called when device enrollment was successful.
}
@Override
public void onFailure(final MechanismCreationException e) {
// called when device enrollment has failed.
}
});
guard let fraClient = FRAClient.shared else {
print("FRAuthenticator SDK is not initialized")
return
}
fraClient.createMechanismFromUri(uri: url, onSuccess: { (mechanism) in
// Method call occurs when device enrollment is successful.
}, onError: { (error) in
// Method call occurs when device enrollment fails.
})
Handle push notifications from the server
Your app that uses the Ping (ForgeRock) Authenticator module needs to respond to incoming push notifications, and ask the user to either accept or reject the authentication.
-
Android
-
iOS
Receive FCM Push notifications by using FirebaseMessagingService#onMessageReceived
.
To handle RemoteMessage
, use the FRAClient.handleMessage()
method:
public void onMessageReceived(final RemoteMessage message) {
PushNotification notification = fraClient.handleMessage(message);
}
Receive Apple push notifications by using the application()
method in AppDelegate
.
To handle RemoteNotification
, use the FRAPushHandler.shared.application(:didReceiveRemoteNotification)
method.
The method returns a PushNotification
object, which contains the accept
and deny
methods to handle the authentication request:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// Once you receive the remote notification, handle it with FRAPushHandler to get the PushNotification object.
// If RemoteNotification does not contain the expected payload structured from {am_name}, the Authenticator module does not return the PushNotification object.
if let notification = FRAPushHandler.shared.application(application, didReceiveRemoteNotification: userInfo) {
// With the PushNotification object, you can either accept or deny
notification.accept(onSuccess: {
}) { (error) in
}
}
}
Obtain values from the push notification payload
The pushNotification
class provide the following methods for obtaining values from the payload received in the push notification:
Android method | iOS method | Description |
---|---|---|
|
|
Returns a JSON string containing the values specified in the Custom Payload Attributes property of the Push Sender node. |
|
|
Returns the string specified in the User Message property of the Push Sender node, such as |
|
|
Returns a JSON string containing additional context information when the Share Context info property is enabled in the Push Sender node. Possible attributes in the JSON string are as follows:
Ensure you check these attributes for Example:
|
|
|
Returns a PushType enum value that specifies the type of push notification to present to the user. This value is based on the configuration of the Push Type property in the Push Sender node. Possible values are:
|
|
|
Returns an array of integers that matches those displayed on the login screen and populates the |
|
|
Returns the timestamp of when the authentication server generated the push authentication payload. |
Handle different push notification types
Use code similar to the following to determine which push type was requested in the payload:
-
Android
-
iOS
if (notification.getPushType() == PushType.CHALLENGE) {
notification.accept(choice, listener);
} else if (notification.getPushType() == PushType.BIOMETRIC) {
notification.accept(null, null, true, activity, listener);
} else {
notification.accept(listener);
}
if notification.pushType == .challenge {
notification.accept(
challengeResponse: "34",
onSuccess: successCallback,
onError: errorCallback
)
} else if notification.pushType == .biometric {
notification.accept(
title: "title",
allowDeviceCredentials: true,
onSuccess: successCallback,
onError: errorCallback
)
} else {
notification.accept(
onSuccess: successCallback,
onError: errorCallback
)
}
Handle the default push type
The PushNotification
class or object provides an accept
method for handling a PushType.default
authentication request:
-
Android
-
iOS
pushNotification.accept(new FRAListener<Void>() {
@Override
public void onSuccess(Void result) {
// called when accepting the push authentication request was successful.
}
@Override
public void onFailure(final PushAuthenticationException e) {
// called when denying the push authentication request, or it has failed.
}
});
pushNotification.accept(onSuccess: {
// called when accepting the push authentication request was successful.
}, onError: { (error) in
// called when denying the push authentication request, or it has failed.
})
Handle the challenge push type
For PushType.challenge
authentication requests, use the following accept
method that receives the challenge as a parameter:
-
Android
-
iOS
public final void accept(
@NonNull String challengeResponse,
@NonNull FRAListener<Void> listener
) {}
public func accept(
challengeResponse: String,
onSuccess: @escaping SuccessCallback,
onError: @escaping ErrorCallback
) {}
Handle the biometric push type
For PushType.biometric
authentication requests, use the following accept
method that processes the biometric authentication request:
-
Android
-
iOS
@RequiresApi(Build.VERSION_CODES.M)
public final void accept(
String title,
String subtitle,
boolean allowDeviceCredentials,
@NonNull AppCompatActivity activity,
@NonNull FRAListener<Void> listener
) {}
public func accept(
title: String,
allowDeviceCredentials: Bool,
onSuccess: @escaping SuccessCallback,
onError: @escaping ErrorCallback
) {}