ForgeRock Developer Experience

Integrate MFA using push notifications

Applies to:

  • ForgeRock SDK for Android

  • ForgeRock SDK for iOS

  • ForgeRock SDK for JavaScript

This topic explains how to integrate support for push authentication into your projects that use the ForgeRock Authenticator module.

Prerequisites

To integrate push authentication into your application that uses the ForgeRock Authenticator module, ensure you have completed the following tasks first:

  1. Configure your ForgeRock server to operate with the ForgeRock Authenticator.

  2. Provision credentials for Amazon Simple Notification Service (SNS) on Backstage.

  3. Configure your ForgeRock server with the settings it needs to be able to send push notifications to mobile devices.

  4. Create an authentication journey that registers the device and then sends a push notification to that device.

  5. Integrate the ForgeRock Authenticator module into your app.

  6. Start the ForgeRock Authenticator module in your app.

Sample apps

You can find example source code for integrating push authentication in the sample authenticator application repositories on GitHub:

Step 1. Register the app for notifications

The ForgeRock Authenticator module uses an Apple or Google service to receive push notifications.

Each instance of your application requires a unique device registration token to receive the push notifications from your ForgeRock server.

For information on obtaining the correct token, refer to the following:

When your app has obtained the neccessary token, enable push notifications as follows:

  • Android

  • iOS

fraClient.registerForRemoteNotifications("fcm-device-registration-token");
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)
}

Step 2. Register your app

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 obtaining 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.
})

Step 3. Handle push notifications

Your app that uses the 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(_:didReceiveRemoteNotification:fetchCompletionHandler:) 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, 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

getCustomPayload()

customPayload

Returns a JSON string containing the values specified in the Custom Payload Attributes property of the Push Sender node.

getMessage()

message

Returns the string specified in the User Message property of the Push Sender node, such as Login attempt from Demo at ForgeRock.

getContextInfo()

contextInfo

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:

  • location

  • userAgent

  • remoteIp

Ensure you check these attributes for null values, as they depend on being able to be collected by the Device Profile Collector node.

Example:

{
  "location": {
    "latitude": 51.4517076,
    "longitude": -2.5950234
  },
  "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36",
  "remoteIp": "198.51.100.23"
}

getPushType()

pushType

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:

PushType.default

Requires the user to tap to accept.

PushType.challenge

Requires the user to select one of three numbers displayed on their device.

This selected number must match the code displayed in the browser for the request to be verified.

PushType.biometric

Requires the user’s biometric authentication to process the notification.

getNumbersChallenge()

numbersChallengeArray

Returns an array of integers that matches those displayed on the login screen and populates the numbersChallenge attribute, if the Push Type property in the Push Sender node is set to Display Challenge Code.

timeAdded

timeAdded

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
) {}

More information

Refer to the following links for information on some of the interfaces and objects used in this topic:

Android iOS

PushNotification

PushNotification

PushMechanism

PushMechanism

PushType

PushType

Copyright © 2010-2024 ForgeRock, all rights reserved.