PingOne Native SDKs

The PingOne Native SDKs are native iOS and Android client SDKs for PingOne services built to interact with the PingOne Platform API. Currently, native SDKs are available for these services:

PingOne MFA Native SDKs

The PingOne Native SDK provides the ability to integrate PingOne MFA functionality into your native applications. Topics include:

PingOne MFA Native SDK flows

Pairing - automatic enrollment

The automatic enrollment flow requires as little as one extra step from the user. The first time the user logs into an application which has an embedded PingOne Native SDK component, they are asked if they wish to trust that device. Once they approve, PingOne works behind the scenes, without requiring anything else from the user.

During user authentication, a native app communicates with the PingOne platform to generate a token. The token allows pairing the device to the user in the context of a customer application. The user is not required to type or scan anything.

  1. The user is identified on the customer native application, usually with a unique user identifier, for example, a username.
  2. The PingOne Native SDK returns a native payload to the customer native application. The payload is a small data package created by the PingOne Native SDK component, which is used as part of the device's authorization.
  3. The customer native application sends an authentication request to the PingOne Platform, including the native payload.
  4. The customer native application receives an ID token.
  5. The customer native application passes the ID token to the PingOne Native SDK.
  6. The PingOne Native SDK returns a pairing object to the customer native application, to pair or ignore the device.
  7. The customer native application prompts the user for the approve or deny action via a dialog. Based on the user's choice, the customer native application notifies PingOne Native SDK.
  8. The PingOne Native SDK completes the transaction accordingly, by communicating directly with PingOne Platform.

Implement automatic pairing of native app as MFA authenticator app

In order to enable automatic pairing of a native app as an MFA authenticator app, there are several tasks that must be coordinated between admin and developers.
In brief, you will do the following:

  • Create a native application with Authenticator configuration.
  • Configure a sign-on policy with MFA step where the native application is configured as an authenticator.
  • Assign the sign-on policy to the native app.
  • Write code in the application to support automatic app enrollment.

Follow the detailed steps below:

Supply the relevant details for the admin to do the following in the PingOne admin console:

Admin tasks
  1. Create a native app. See Add an application - Native.
  2. In Edit an application, in the Authenticator tab:
    • Add the Package name (Android) and Bundle ID (iOS) of your native application.
    • Configure the push credentials per platform.
  3. Create a sign-on policy, and add an MFA step. See Add an authentication policy.
  4. In the MFA step, mark the Native Applications checkbox, and mark the native application created in step 1.
  5. In the native application's Policies tab, choose the sign-on policy you created.
  6. In Edit an authentication policy, in the MFA step, under the native application name, mark the Auto Enrollment checkbox. (Note that steps 1-5 are always required for native authentication, for either push notification or device authorization.)
Developer tasks

In your native application code (also described in the iOS and Android README.md files, see PingOne Native SDK for iOS or PingOne Native SDK for Android):

  1. Get the native payload from the SDK (PingOne.generateMobilePayload())
  2. Pass the received payload of the OIDC request to the authorization service, as the mobilePayload query parameter.
  3. Call processIdToken() with the token you received from PingOne platform.
  4. If automatic pairing is triggered (i.e. the user was not already paired with the device), processIdToken() will return a pairing object with approve() and deny() functions. Calling approve() will pair the user with the device.

Automatic device authorization

During automatic device authorization, a native app communicates with the PingOne platform to generate a token. The token allows authenticating the user device in the context of a customer application. The user is not aware of this, and is not required to type or scan anything.

  1. The customer native app requests a native payload from the PingOne Native SDK.
  2. The PingOne Native SDK returns a native payload to the customer native application.
  3. The customer native application sends an authentication request to the PingOne Platform, including the native payload.
  4. If the platform's authentication flow (sign-on policy) contains an MFA step, and extra verification is disabled, the platform verifies that the native device is paired and active, authenticates the native device, and the MFA step succeeds. The flow skips to step 9.
  5. The platform verifies that the native device is paired and active and sends a "silent" push notification to the native application via the APNS/GCM notification service.
  6. The native application passes the "silent" notification to the PingOne Native SDK.
  7. The PingOne Native SDK acknowledges receiving the "silent" push by sending a confirmation directly to the PingOne platform.
  8. The platform authenticates the native device, and the MFA step succeeds.
  9. The customer native application receives an ID token from the PingOne Platform.
  10. The customer native application passes the ID token to the PingOne Native SDK.
  11. The PingOne Native SDK returns null to the customer native application, indicating that authorization has completed and no further action needs to be taken.

Implement automatic device authorization

Admin tasks

The admin configuration that was implemented for automatic enrollment, is applied to automatic device authorization.

  1. Create a native app, or edit an existing app. See Add an application - Native.
  2. In Edit an application, in the Authenticator tab:
    • Add the Package name (Android) and Bundle ID (iOS) of your native application.
    • Configure the push credentials per platform.
  3. Create a sign-on policy, and add an MFA step. See Add an authentication policy.
  4. In the MFA step, mark the Native Applications checkbox, and mark the native application created in step 1.
  5. In the native application's Policies tab, choose the sign-on policy you created.
  6. In Edit an authentication policy > Add a multi-factor authentication step, in the MFA step, under the native application name, mark the Device Authorization checkbox.
  7. Configure Extra Verification: Leave the Extra Verification checkbox unchecked to disable extra verification, or mark the checkbox to select the Permissive or Restrictive modes. (Note that steps 1-5 are always required for native authentication, for either manual or automatic authorization.)
Developer tasks

In your native application code (also described in the iOS and Android README.md files, see PingOne Native SDK for iOS or PingOne Native SDK for Android):

  1. Get the native payload from the SDK (PingOne.generateMobilePayload())
  2. Pass the received payload of the OIDC request to the authorization service, as the mobilePayload query parameter.
  3. Call processIdToken() with the token you received from PingOne platform.
  4. If device authorization is triggered (i.e. verification that the user's device is already paired and active), processIdToken() will return null, indicating that authorization has completed and no further action needs to be taken.

Authentication code flow

The authentication code flow enables users to sign on without any data entry, such as providing a username, password, or entering a one-time passcode. The user scans the code, and if successful, gains access to an application's services.

During an authentication code flow, a native app communicates with the PingOne platform to initiate authentication with an authentication code, which can be a QR code. The flow starts with a call to the Create Authentication Code endpoint. The Mobile SDK validates the code value and returns an AuthenticationObject to the mobile app.

  1. The customer mobile app requests an authentication code flow from the PingOne Mobile SDK.
  2. The Mobile SDK requests validation of the authentication code value from the PingOne platform.
  3. If the authentication code value is validated by the PingOne platform, the Mobile SDK creates an AuthenticationObject.
  4. The Mobile SDK returns the AuthenticationObject to the customer mobile app.
  5. If the status property value of the AuthenticationObject is CLAIMED, the flow continues. For all other status values, the flow ends.
  6. If the number of users is two or more, the flow presents the user with an option to choose a specific user from the list. If the AuthenticationObject specifies only one user, the flow continues without requiring this step.
  7. If the user approval setting has a value of REQUIRED, the mobile app prompts the user to approve or deny the current authentication session. If user approval is set to NOT_REQUIRED, the authentication flow skips this step.
  8. The customer mobile app makes a validation request to the Mobile SDK.
  9. The Mobile SDK claims authentication with the selected user and queries the PingOne server.
  10. The PingOne server returns an authentication status.

Implement an authentication code flow

Admin tasks

To configure direct triggering of the mobile application in an authentication code flow:

  1. Create a native app, or edit an existing app. See Add an application - Native.
  2. Use the Authenticator tab to allow mobile authentication for the app. If your organization uses the PingOne MFA SDK to allow authentication with a QR code in certain flows, the admin can optionally configure a universal link or schema app link to enable direct triggering of the mobile application when scanning a QR code with a QR scanner. See Editing an application - Native.
Developer tasks

In your native application code (also described in the iOS and Android README.md files, see PingOne Native SDK for iOS or PingOne Native SDK for Android):

  1. Pass the retrieved authentication code to the mobile SDK by calling PingOne.authenticate().
  2. Parse the retrieved AuthenticationObject. If the status field equals CLAIMED, proceed to the next step.
  3. If the number of users is more than one and/or the value of the field needsApproval equals REQUIRED, the AuthenticationObject will contain approve() and deny() options.
  4. Call the approve() method of the AuthenticationObject with the corresponding userId to complete the authentication.

PingOne MFA SDK for Android

Overview

The PingOne MFA Native SDK provides the ability to integrate PingOne MFA functionality into your native applications.

The PingOne MFA Native SDK API for Android is included in the SDK package. The functions, parameters and error codes are listed below.

The PingOne MFA Native SDK package is available for download at https://github.com/pingidentity/pingone-mobile-sdk-android. Further details for setup and integrating PingOne MFA Native SDK into your native apps are available in the README file in the Android folder of the downloadable package.

The PingOne SDK for Android supports the following software versions:

  • Android 8 (API 26) and greater

See Edit an application in the admin guide for the server-side configuration steps.

PingOne MFA Native SDK sample app

The PingOne MFA Native SDK bundle provides a sample app that includes all the basic flows in order to help you get started.

The sample app package for Android is available for download at https://github.com/pingidentity/pingone-mobile-sdk-android. Further details are available in the README file in the Android folder of the downloadable package.

Authenticator sample app

The Authenticator sample app is a native app that has the sole function of performing strong authentication. It provides a simple example for developers and solution architects, to enable easy and rapid deployment of an authenticator app with minimal effort.

For scenarios which solely require creation of a native authenticator rather than a full native native app, the Authenticator sample app offers a passwordless and secured solution, that only requires compilation of the sample with customer's branding and credentials, and uploading it to the app store.

The Authenticator sample app package for Android is available for download at https://github.com/pingidentity/pingone-authenticator-sample-app-android/. Further details are available in the README file.

Mobile device integrity check

PingOne has an integrated mobile device integrity check in its MFA flows, that allows mobile applications to deny access when a mobile device is suspected to be compromised.

Mobile device integrity check - admin UI configuration

Each application must be set up and configured in the organization's PingOne environment, either in the admin UI, or using the MFA devices API. The development team provides the admin with the application name and details.

The admin's configuration of device integrity detection is detailed in the PingOne admin guide. See Editing an application - Native.

Mobile device integrity check - Android implementation

PingOne uses Google’s SafetyNet attestation API to determine the integrity of the mobile device it is running on. See Google’s SafetyNet Attestation documentation for a better understanding of SafetyNet’s capabilities.

Mobile app developers are responsible for enabling and monitoring SafetyNet's usage.

The following steps are required:

  1. Create a SafetyNet API key:

    See https://developer.android.com/training/safetynet/attestation.html#obtain-api-key. The SafetyNet API key must be provided to the PingOne mobile SDK component.

  2. Request a quota increase:

    See https://support.google.com/googleplay/android-developer/contact/safetynetqr. SafetyNet's quota increase form contains questions that are application dependent, and should be answered by PingOne customers. Select the following options in the SafetyNet quota request:

    • Method of validating SafetyNet Attestation API responses: Server side - by verifying the signing certificate chain
    • Method of automatically retrying in case of errors: Retry with exponential backoff

    The requested estimated quota should take into account your application’s expected traffic, and should also consider the mobile SDK component’s retry and caching policy:

    • The mobile component will not initiate more than one SafetyNet request during the SafetyNet Cached Duration period (as defined in the application’s configuration in the PingOne admin UI), if there was a successful response from SafetyNet which did not fail the integrity test.
    • If a SafetyNet request fails with an error, the mobile component will perform one retry for the user action that triggered the SafetyNet request (for example, a login or request for an OTP).
  3. Monitor SafetyNet:

    To ensure that SafetyNet resources are not exhausted, you are recommended to monitor SafetyNet usage. See Google’s Safetynet Quota and monitoring instructions. If your quota has reached its limit, users could be blocked.

PingOne MFA Native SDK API - Android

PingOne main public class

/**
* Created by Ping Identity
* Copyright © 2020 Ping Identity. All rights reserved.
* Version 1.7.0
*
* Main public class that contains APIs
*/
public class PingOne {
 
   /**
    * Pairs the device with the PingOne server using the pairing key
    * @param context the context of the calling application
    * @param pairingKey pairing key as a String
    * @param callback notifies about a completion of the flow
    *
    * This method is deprecated, please use
    * public static void pair(Context context, String pairingKey, PingOneSDKPairingCallback callback);
    */
   @Deprecated
   public static void pair(Context context, String pairingKey, PingOneSDKCallback callback);
 
   /**
    * Pairs the device with the PingOne server using the pairing key
    * @param context the context of the calling application
    * @param pairingKey pairing key as a String
    * @param callback PingOneSDKPairingCallback that notifies about the completion of the flow
    * Documentation for pairing objecterror codes can be found here:
    * https://apidocs.pingidentity.com/pingone/native-sdks/v1/api/#pingone-mfa-sdk-for-android
    * at the Pairing object error codes section
    */
   public static void pair(Context context, String pairingKey, PingOneSDKPairingCallback callback);
 
   /**
    * When using OpenID Connect, this is a mandatory post action method which should be
    * executed after receiving an id token from the PingOne SDK server.
    * @param idToken: the id token received from the server as a String
    * @param callback: PingOnePairingObjectCallback that notifies about a completion of the flow
    */
   public static void processIdToken(String idToken, PingOnePairingObjectCallback callback);
 
 
   /**
    * Set device remote notification token.
    * @param context the context of the calling application
    * @param fcmRegistrationIdToken the registration token retrieved from FCM
    * @param callback notifies a completion
    */
   public static void setDeviceToken(Context context, String fcmRegistrationIdToken, PingOneSDKCallback callback);
 
   /**
    * Process the remote notification received from PingOne
    * @param remoteMessage a remote message retrieved from FCM
    * @param callback PingOneNotificationCallback notifies about task completion. Will return
    * NotificationObject if the process completed successfully or PingOneSDKError otherwise
    * @deprecated please use processRemoteNotification(Context context, RemoteMessage remoteMessage, PingOneNotificationCallback callback)
    * instead
    */
   @Deprecated
   public static void processRemoteNotification(RemoteMessage remoteMessage, PingOneNotificationCallback callback);
 
   /**
    * Process the remote notification received from PingOne
    * @param context the context of the calling application
    * @param remoteMessage a remote message retrieved from FCM
    * @param callback PingOneNotificationCallback notifies about task completion. Will return
    * NotificationObject if the process completed successfully or PingOneSDKError otherwise
    */
   public static void processRemoteNotification(Context context, RemoteMessage remoteMessage, PingOneNotificationCallback callback);
 
   /**
    * Returns a payload string for any communication needed between the customer native app
    * and the PingOne SDK server.
    * @param context the context of the calling application
    */
   public static String generateMobilePayload(Context context);
 
   /**
    * Returns all the paired users from the PingOne server.
    * @param context the context of the calling application
    * @param callback a callback that will be triggered at completion
    */
   public static void getInfo(Context context, PingOneGetInfoCallback callback);
 
   /**
    * Send logs to the PingOne server.
    * @param context the context of the calling application
    * @param callback a callback that will be triggered at completion
    */
   public static void sendLogs(Context context, PingOneSendLogsCallback callback);
 
   /**
    * Requests the SDK to provide One Time Passcode
    * @param context the context of the calling application
    * @param callback a callback that will be triggered at completion. Will contain the passcode data.
    */
   public static void getOneTimePassCode(Context context, PingOneOneTimePasscodeCallback callback);
 
   /**
    * Method that will notify the server not to send push messages to this device
    * @param context the context of the calling application
    * @param allowPush a boolean that will tell the server to send push messages or not. Defaults to true.
    */
   public static void allowPushNotifications(Context context, boolean allowPush);
 
   /**
    * PingOne uses Google's SafetyNet to perform device integrity validation for threat protection.
    * To use this feature, you should obtain a SafetyNet API Key. The retrieved API key should be passed
    * to the PingOne SDK using the following method
    * @param context the context of the calling application
    * @param apiKey an API key retrieved from the SafetyNet
    */
   public static void setSafetyNetApiKey(Context context, String apiKey);
  
   /**
    * Method that will try to authenticate with given authCode value
    * @param context the context of the calling application
    * @param authCode the authentication code parsed from QR or manual input
    * @param callback PingOneAuthenticationCallback that is triggered when the request is complete
    */
   public static void authenticate(Context context, String authCode,
                                   PingOneAuthenticationCallback callback);
  
  
   /**
    * Callback interface that will notify developers about asynchronous task completion
    * PingOneSDKError object will be null if the process completed successfully
    */
   public interface PingOneSDKCallback {
       void onComplete(@Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will notify developers about asynchronous task completion
    * PingOneSDKError object will be null if the process completed successfully
    * A wrapper object that contains an ArrayList with authentication methods (one-time passcode and Push)
    * will be null if the process completed with error.
    */
   public interface PingOneSDKPairingCallback extends PingOneSDKCallback{
       void onComplete(@Nullable PairingInfo pairingInfo, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will notify developers about processRemoteNotification task completion
    * NotificationObject will be returned if the process completed successfully
    * PingOneSDKError object will be null if the process completed successfully
    */
   public interface PingOneNotificationCallback {
       void onComplete(@Nullable NotificationObject notificationObject, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will notify developers about processIdToken task completion
    * PairingObject will be returned if the process completed successfully
    * PingOneSDKError object will be null if the process completed successfully.
    * onComplete(null, null) will be triggered on OIDC login with already paired device
    */
   public interface PingOnePairingObjectCallback {
       void onComplete(@Nullable PairingObject pairingObject, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will notify developers about getInfo task completion
    * JsonObject containing array with all the paired users info will be returned
    * to the calling application on successful response.
    * PingOneSDKError object will be null if the process completed successfully.
    */
   public interface PingOneGetInfoCallback {
       void onComplete(@Nullable JsonObject publicJson, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will notify developers about sendLogs task completion
    * String with unique support ID will be returned in case the logs were sent successfully.
    * The supportId can then be passed to the Ping Identity support team for review.
    * PingOneSDKError object will be null if the process completed successfully.
    */
   public interface PingOneSendLogsCallback {
       void onComplete(@Nullable String supportId, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will return a one-time passcode object.
    * PingOneSDKError object will be null if the process completed successfully.
    * OneTimePasscodeInfo object will be null if the process did not complete successfully.
    */
   public interface PingOneOneTimePasscodeCallback {
       void onComplete(@Nullable OneTimePasscodeInfo oneTimePasscodeInfo, @Nullable PingOneSDKError error);
   }
  
   /**
    * Callback interface that will return AuthenticationObject.
    * PingOneSDKError object will be null if the process completed successfully.
    * AuthenticationObject object will be null if the process did not complete successfully.
    */
   public interface PingOneAuthenticationCallback {
       void onComplete(@Nullable AuthenticationObject authenticationObject, @Nullable PingOneSDKError error);
   }
 
   /**
    * Callback interface that will return the final status of the userless authentication flow.
    * PingOneSDKError object will be null if the process completed successfully.
    * Status String will be null if the process did not complete successfully.
    */
   public interface PingOneAuthenticationStatusCallback{
       void onComplete(@Nullable String status, @Nullable PingOneSDKError error);
   }
 
}

NotificationObject

/**
* Created by Ping Identity
* Copyright © 2020 Ping Identity. All rights reserved.
* Version 1.3.0
*
* NotificationObject represents an authentication request via remote notification.
* It can be approved or denied.
* Implements Parcelable so the developer will be able to pass it via intents in the application.
*/
public class NotificationObject implements Parcelable {
	
	/**
	* Approve authentication
	* @param context application context
	* @param callback a callback notifies a completion
	* Deprecated in version 1.1.0 please use approve(Context, String, PingOneSDKCallback) instead
	*/
	@Deprecated
	public void approve(Context context, final PingOneSDKCallback callback) ;

    /**
	* Approve authentication
	* @param context application context
	* @param authenticationMethod a String representing the authentication method that was used to 
	* authenticate the user on the authenticating device, for example, fpt (fingerprint), 
	* face (facial recognition) etc. Refer to the "amr" (Authentication Method Reference) 
	* values in https://tools.ietf.org/html/rfc8176/#section-2.
	* @param callback a callback notifies a completion
	*/
	public void approve(Context context, String authenticationMethod, final PingOne.PingOneSDKCallback callback);

	/**
	* Denies authentication
	* @param context application context
	* @param callback a callback notifies a completion
	*/
	public void deny(Context context, final PingOneSDKCallback callback);

	/**
     * The maximum time duration allowed from getting push till user response
     * @return int amount of seconds
     */
     public int getTimeoutDuration();

	/**
     * Returns client-context String object or null
     * if no context was passed by the server
     * @return client context as String
     */
    public String getClientContext();
}

PairingObject

/**
* Created by Ping Identity
* Copyright © 2020 Ping Identity. All rights reserved.
* Version 1.3.0
*
* an object returned if processIdToken flow proposes an automatic pairing
* to allow automatic pairing the developer should trigger the approve method of the object
*/
public class PairingObject implements Parcelable {
	
	/**
	* Approve pairing
	* @param context application context
	* @param callback a callback notifies a completion
	*/
	public void approve(Context context, final PingOneSDKCallback callback);
}

AuthenticationObject

/**
 * Created by Ping Identity
 * Copyright © 2022 Ping Identity. All rights reserved.
 *
 * AuthenticationObject an object returned if the user called the authenticate API's method.
 * Implements Parcelable so the developer will be able to pass it via intents.
 */
public class AuthenticationObject implements Parcelable {
    
    /**
     * Approve authentication
     * @param context - the context of calling application
     * @param userId - String value of the user id, fetched from the user object.
     * @param callback - PingOneAuthenticationStatus callback will 
     * notify about completion
     */
    public void approve(Context context, String userId, PingOne.PingOneAuthenticationStatusCallback callback);
    
    /**
     * Deny authentication
     * @param context - the context of the calling application
     * @param userId - String value of the user id, fetched from the user object. 
     * This field can be null, authentication can be denied without passing this value.
     * @param callback - PingOneAuthenticationStatus callback will 
     * notify about completion
     */
    public void deny(Context context, String userId, PingOne.PingOneAuthenticationStatusCallback callback);

    /**
     *  returns an array of paired users as a JsonArray
     */
    public JsonArray getUsers();

    /**
     * returns a client context value as a String
     */
    public String getClientContext();

    /**
     * The status String value returned from a server when the user calls an authenticate API method.
     * Possible values:
     * CLAIMED
     * EXPIRED
     * DENIED
     * COMPLETED
     * @return status of the code used for QR authentication
     */
    public String getStatus();
    

    /**
     * Possible values:
     * REQUIRED
     * NOT_REQUIRED
     * returns "REQUIRED" if user approval is required to complete the authentication and
     * "NOT_REQUIRED" otherwise
     */
    public String getNeedsApproval();
}

PingOneSDKError

/**
* Class that describes an error by error code and error message
*/
public class PingOneSDKError {
	public int getCode();
	public String getMessage();
}

Pairing object error codes

The pairing object always returns both a push authenticator status and an OTP status. If the status is FAILED, a Push authenticator error code and message or an OTP error code and message is returned with the status.

Push authenticator error codes

Error code Description
Any error string returned from FCM, for example Unregistered See more error codes and descriptions in Error codes for FCM failure conditions in the Firebase portal.
MissingPushCredentials There are no push credentials on the PingOne server.
MissingDeviceToken There is no device token on the PingOne server.
PushDisabled The push was disabled via the native SDK API.

OTP error codes

Error code Description
OneTimePasscodeRetriesExceeded On device pairing, the OTP can be verified within 3 attempts.
On checkOTP, the OTP can be verified within 3 attempts in every 15 minutes.
InvalidOneTimePasscode The user entered an invalid OTP.
OneTimePasscodeExpired On device pairing, the OTP can be verified within 15 minutes.
On checkOTP, there are no limitations except for OneTimePasscodeRetriesExceeded.
UnSynchronizedClock The native clock not synchronized with the generated secret.

PingOne MFA Native SDK API error codes

Error Status Description
10000 INTERNAL_ERROR Internal Error
10001 DEVICE_TOKEN_IS_MISSING Device token was missing and is required to complete this action.
10002 UNRECOGNIZED_REMOTE_NOTIFICATION Remote notification isn't from PingOne.
10003 SERVER_ERROR There was a server error.
10004 NO_CONNECTIVITY There was a problem with the network.
10005 PAIRING_KEY There was a problem with the pairing key.
10006 BUNDLE_ID There was a problem with the application ID.
10007 PAIRING_KEY_DATA_CENTER_MISMATCH Device may be paired in one regional data center only, and is already paired in another regional data center.
10008 DEVICE_IS_NOT_PAIRED Device is not paired.
10009 PUSH_CONFIRMATION_TIMEOUT Client confirmation delay responding to push notification, resulted in timeout.
10010 PASSCODE_NOT_VALID Client failed to verify due to invalid one time passcode sent to the server.
10011 AUTHENTICATION_CODE_INVALID Authentication code is not valid.
10012 FAILED_POLICY_REQUIREMENTS Mobile device does not comply with policy requirements.

PingOne SDK for iOS

Overview

The PingOne MFA Native SDK provides the ability to integrate PingOne MFA functionality into your native applications.

The PingOne MFA Native SDK API for iOS is declared in the PingOne.h header file included in the SDK package. This header file (replicated below) includes descriptions for all functions, parameters and error codes.

The PingOne MFA Native SDK package is available for download at https://github.com/pingidentity/pingone-mobile-sdk-ios. Further details for setup and integrating PingOne MFA Native SDK into your native apps are available in the README file.

The PingOne SDK for iOS supports the following software versions:

  • Xcode 13 and above
  • iOS 12.0 and above

See Edit an application in the admin guide for the server-side configuration steps.

PingOne MFA Native SDK sample app

The PingOne MFA Native SDK bundle provides a sample app that includes all the basic flows in order to help you get started.

The sample app package for iOS is available for download at https://github.com/pingidentity/pingone-mobile-sdk-ios. Further details are available in the README file.

Authenticator sample app

The Authenticator sample app is a native app that has the sole function of performing strong authentication. It provides a simple example for developers and solution architects, to enable easy and rapid deployment of an authenticator app with minimal effort.

For scenarios which solely require creation of a native authenticator rather than a full native native app, the Authenticator sample app offers a passwordless and secured solution, that only requires compilation of the sample with customer's branding and credentials, and uploading it to the app store.

The Authenticator sample app package for iOS is available for download at https://github.com/pingidentity/pingone-authenticator-sample-app-ios. Further details are available in the README file.

Mobile device integrity check

PingOne has an integrated mobile device integrity check in its MFA flows, that allows mobile applications to deny access when a mobile device is suspected to be compromised.

Mobile device integrity check - admin UI configuration

Each application must be set up and configured in the organization's PingOne environment, either in the admin UI, or using the MFA devices API. The development team provides the admin with the application name and details.

The admin's configuration of device integrity detection is detailed in the PingOne admin guide. See Editing an application - Native.

PingOne MFA Native SDK API - iOS (PingOne.h)

//
//  PingOne.h
//  PingOne
//
//  Created by Ping Identity on 3/12/19.
//  Copyright © 2019 Ping Identity. All rights reserved.
//

/// Public class that contains data of authenticate with code response
@objc public class AuthenticationObject : NSObject {

    /// List of users that are paired to this device. Each user object can contain the following String variables: `userId`, `email`, `given`, `family` and `username`.
    @objc public var users: [[String : Any]]?

    /// String that determines if user approval is required to complete an authentication. Possible values: `REQUIRED` and `NOT_REQUIRED`
    @objc public var userApproval: String?

    /// Object for passing any data as a String from server to end-user
    @objc public var clientContext: String?

    /// String value returned from a server when a user calls an authenticate API method. Possible values: can be `CLAIMED`, `EXPIRED`, `DENIED` or `COMPLETED`
    @objc public var status: String?

    /// Approve authentication with code
    ///
    /// If `userApproval` is `REQUIRED` or multiple users may approve the authentication,
    /// call this method with a userId of the user, who triggered the method.
    /// - Parameter userId: String value of the user id, fetched from the user object.
    /// - Returns completionHandler: Will return status String value with one of the following values:
    /// `COMPLETED` or `EXPIRED`.
    /// Returns NSError in case of an error.
    @objc final public func approve(userId: String, completionHandler: @escaping (_ status: String?, _ error: NSError?) -> Void)

    /// Deny authentication with code
    ///
    /// If `userApproval` is `REQUIRED` or multiple users may deny the authentication,
    /// call this method with a userId of the user, who triggered the method.
    /// - Parameter userId: String value of the user id, fetched from the user object.
    /// This field is not mandatory, authentication can be denied without passing this value.
    /// - Returns completionHandler: Will return status String value with one of the following values:
    /// `DENIED` or `EXPIRED`.
    /// Returns NSError in case of an error.
    @objc final public func deny(_ userId: String? = nil, completionHandler: @escaping (_ status: String?, _ error: NSError?) -> Void)
}

/// AuthenticatorModel public class contains the acceptable authorization methods and its status
@objc public class AuthenticatorModel : NSObject {

    @objc public enum AuthenticatorType : NSInteger {

        /// Unknown type of authenticator
        case unknown = -1

        /// Authenticator with push type
        case push = 0

        /// Authenticator with one time passcode type
        case oneTimePasscode
    }

    @objc public enum AuthenticatorStatus : NSInteger {

        /// Unknown authenticator status
        case unknown = -1

        /// Authenticator received completed status
        case completed = 0

        /// Authenticator received failed status
        case failed
    }

    /// The Authenticator type
    @objc final public let type: PingOneSDK.AuthenticatorModel.AuthenticatorType

    /// The Authenticator status
    @objc final public let status: PingOneSDK.AuthenticatorModel.AuthenticatorStatus

    /// Error message in case of authorization method failure
    @objc final public let errorCode: String?

    /// Error code in case of authorization method failure
    @objc final public let errorMessage: String?
}

@objc public enum ErrorCode : NSInteger {

    /// Internal Error.
    case internalError = 10000

    /// Device token was missing and is required to complete this action.
    case deviceTokenIsMissing = 10001

    /// Remote notification isn't from PingOne.
    case unrecognizedRemoteNotification = 10002

    /// There was a server error.
    case serverError = 10003

    /// There was a problem with the network.
    case noConnectivity = 10004

    /// There was a problem with the pairing key.
    case pairingKey = 10005

    /// There was a problem with the bundle id.
    case bundleId = 10006

    /// Device may be paired in one regional data center only, and is already paired in another regional data center.
    case pairingKeyDataCenterMismatch = 10007

    /// Device is not paired.
    case deviceIsNotPaired = 10008

    /// Client confirmation delay responding to push notification, resulted in timeout.
    case pushConfirmationTimeout = 10009

    /// Client failed to verify due to invalid one time passcode sent to the server.
    case passcodeNotValid = 10010

    /// Authentication code is not valid.
    case authCodeInvalid = 10011
    
    /// Mobile device does not comply with policy requirements.
    case failedPolicyRequirements = 10012
}

/// A Notification Object represents an authentication request via remote notification. It can be approved or denied.
@objc public class NotificationObject : NSObject {

    /// Type that describes the purpose of the notification.
    @objc public enum NotificationType : NSInteger {

        /// Notification was not recognized.
        case none = -1

        /// Notification process has finished. No further action is required.
        case done

        /// Authentication notification that should be presented to the user and followed with the `approve` or `deny` method.
        case authentication
    }

    /// Describes the purpose of the notification.
    @objc final public let notificationType: PingOneSDK.NotificationObject.NotificationType

    /// The maximum time duration allowed from getting push till user response.
    @objc final public let timeoutDuration: Int

    /// Extra parameters are passed to the client with the push object
    @objc final public let clientContext: String

    /// Approve authentication
    /// - Parameters:
    ///   - withAuthenticationMethod: The `String` is the authentication method that was used to authenticate the user on the authenticating device, for example, fpt (fingerprint), face (facial recognition) etc. Refer to the "amr" (Authentication Method Reference) values in https://tools.ietf.org/html/rfc8176/#section-2 .
    ///   - completionHandler: Will return NSError in case of an error.
    @objc final public func approve(withAuthenticationMethod: String?, completionHandler: @escaping (_ error: NSError?) -> Void)

    /// Approve authentication
    ///
    /// - Parameter completionHandler: Will return NSError in case of an error.
    @available(*, deprecated, message: "please use approve(withAuthenticationMethod:completionHandler) instead")
    @objc final public func approve(completionHandler: @escaping (_ error: NSError?) -> Void)

    /// Deny authentication
    ///
    /// - Parameter completionHandler: Will return NSError in case of an error.
    @objc final public func deny(completionHandler: @escaping (_ error: NSError?) -> Void)
}

/// Public class that contains data of the One Time Passcode
@objc public class OneTimePasscodeInfo : NSObject {

    /// The generated passcode
    @objc final public let passcode: String

    /// The time of expiration of the code.
    /// it is provided in double format which holds the future time in milliseconds divided by 1000 to represent seconds
    @objc final public let validUntil: Double

    /// The time of creation of the passcode.
    @objc final public let validFrom: Double

    /// The total life span of the passcode since it was created until it expired. The time unit is seconds
    @objc final public let timeWindowSize: Int
}

/// Public PairingInfo class contains data about pairing resolution
@objc public class PairingInfo : NSObject {

    /// List of authentication methods and their statuses
    @objc public var authenticatorsArray: [PingOneSDK.AuthenticatorModel]
}

/// PairingObject represents a pairing request that can be approved or ignored.
@objc public class PairingObject : NSObject {

    /// Approve pairing
    ///
    /// - Parameter completionHandler: Will return NSError in case of an error.
    @available(*, deprecated, message: "please use approve(completion: @escaping (_ response: PairingInfo?, _ error: NSError?) -> Void)")
    @objc final public func approve(completionHandler: @escaping (_ error: NSError?) -> Void)

    /// Approve pairing
    ///
    /// - Parameter completionHandler: Will return PairingInfo object containing data about pairing resolution, and NSError in case of an error.
    @objc final public func approve(completion: @escaping (_ response: PingOneSDK.PairingInfo?, _ error: NSError?) -> Void)
}

@objc public class PingOne : NSObject {

    /// Type that describes the APNS environment.
    ///
    /// It is possible to establish a connection between the PingOne server and one of the following Apple servers:
    /// Development (`sandbox`) or `production` server.
    @objc public enum APNSDeviceTokenType : NSInteger {

        /// Production environment.
        case production = 0

        /// Sandbox environment.
        case sandbox
    }

    /// Generate a payload string for any communication needed between the customer mobile app and the PingOne server.
    ///
    /// The PingOne Native SDK returns a native payload to the customer native application.
    /// The payload is a small data package created by the PingOne Native SDK component,
    /// which is used as part of the device's authorization.
    ///
    /// - Returns: The mobile payload string.
    ///
    /// - Throws: NSError if failed to create payload.
    @objc public static func generateMobilePayload() throws -> String

    /// Pair device
    ///
    /// Pairs the device with the PingOne server using the `pairing key`.
    ///
    /// - Parameter pairingKey: pairing key as a string.
    ///
    /// - Returns: A completionHandler object which contains an error in case the pair failed.
    @available(*, deprecated, message: "please use pair(_ pairingKey: String, completion: @escaping (_ response: PairingInfo? , NSError?) instead")
    @objc public static func pair(_ pairingKey: String, completionHandler: @escaping (NSError?) -> Void)

    /// Pair device
    ///
    /// Pairs the device with the PingOne server using the `pairing key`.
    ///
    /// - Parameter pairingKey: pairing key as a string.
    ///
    /// - Returns: a `completionHandler` which contains `PairingInfo`, a wrapper object that contains an array list with
    /// authentication methods (One time passcode and Push) status.
    /// In case of error `PairingInfo` will also contain `NSError`.
    /// Documentation for pairing object error codes can be found [here](https://apidocs.pingidentity.com/pingone/native-sdks/v1/api/#pingone-mobile-sdk-for-ios)
    @objc public static func pair(_ pairingKey: String, completion: @escaping (_ response: PingOneSDK.PairingInfo?, NSError?) -> Void)

    /// Pair with automatic enrollment
    ///
    /// When using OpenID Connect, this is a mandatory post action method which should be executed after receiving an ID token
    /// from the PingOne server. If device authorization is triggered (i.e. verification that the user's device
    /// is already paired and active), processIdToken() will return nil, indicating that authorization has completed
    /// and no further action needs to be taken.
    ///
    /// - Parameter idToken: The token `String` value from the OIDExternalUserAgentSession authState object.
    ///
    /// - Returns: Will return PairingObject in case further action is required,
    ///   such as approving the pairing. In case of an error will return NSError.
    @objc public static func processIdToken(_ idToken: String, completionHandler: @escaping (_ pairingObject: PingOneSDK.PairingObject?, _ error: NSError?) -> Void)

    ///  Set device remote notification token
    ///
    ///  Setting should be done within the `application:didRegisterForRemoteNotificationsWithDeviceToken` method
    ///  in the appDelegate class.
    ///
    /// - Parameters:
    ///   - deviceToken: The `Data` received within the `application:didRegisterForRemoteNotificationsWithDeviceToken` method.
    ///   - type: The `APNSDeviceTokenType` enum can be `production` or `sandbox` type.
    ///
    /// - Returns: NSError in case of an error.
    @objc public static func setDeviceToken(_ deviceToken: Data, type: PingOneSDK.PingOne.APNSDeviceTokenType, completionHandler: @escaping (_ error: NSError?) -> Void)

    /// Get PingOne remote notification categories
    ///
    /// PingOne provides the needed categories for notification. The developer may add categories.
    ///
    /// > Tip: setting UNNotificationCategory more than once results in previous settings being overwritten.
    ///
    /// - Returns: The remote notification categories.
    @objc public static func getUNNotificationCategories() -> Set<UNNotificationCategory>

    /// Allow push notification
    ///
    /// Method that will notify the server to send or not to send push messages to this device.
    /// Set to false to disable SDK push notifications.
    ///
    /// - Parameter allowed: a boolean that will tell the server to send push messages or not. Defaults to `true`.
    @objc(allowPushNotifications:) public static func pushNotification(allowed: Bool)

    /// Process the remote notification received from PingOne.
    ///
    /// This method sets the notification data in the SDK and and gets back a PingOne notification object.
    ///
    /// - Parameter userInfo: The `[AnyHashable : Any]` received in the AppDelegate `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)` method.
    ///
    /// - Returns: Will return NSError in case of an error. Will return NotificationObject in case further action is required, such as approving or denying an authentication.
    @objc public static func processRemoteNotification(_ userInfo: [AnyHashable : Any], completionHandler: @escaping (_ notificationObject: PingOneSDK.NotificationObject?, _ error: NSError?) -> Void)

    ///  Process remote notifcation action
    ///
    ///  Tells PingOne server to perform the custom action specified by a remote notification. Should be within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///
    /// - Parameters:
    ///   - identifier: The `String` is the response.actionIdentifier received within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///   - userInfo: The `[AnyHashable : Any]` received within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///   - authenticationMethod: The `String` is the authentication method that was used to authenticate the user
    ///   on the authenticating device, for example, fpt (fingerprint), face (facial recognition) etc.
    ///   Refer to the "amr" (Authentication Method Reference) values [here](https://tools.ietf.org/html/rfc8176/#section-2)
    ///
    /// - Returns: completionHandler that will contain NSError in case of an error.
    /// NotificationObject will be returned in case an action is required.
    @objc public static func processRemoteNotificationAction(_ identifier: String, authenticationMethod: String?, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping (_ notificationObject: PingOneSDK.NotificationObject?, _ error: NSError?) -> Void)

    ///  Get info
    ///
    ///  Returns all the paired users from the PingOne server.
    ///
    /// - Returns: a completionHandler will return NSError in case of an error.
    /// Will return an array with information on all the paired users.
    @objc public static func getInfo(_ completionHandler: @escaping (_ activeUsers: [String : Any]?, _ error: NSError?) -> Void)

    /// Authenticate with code
    ///
    /// PingOne SDK provides an ability to authenticate via scanning the QR code.
    /// The retrieved value should be passed to the PingOne SDK using the following API method.
    ///
    /// - Parameters:
    ///   - authCode: String value of the authentication code, parsed from QR or manual input.
    /// - Returns: a completionHandler that contains `authenticationObject`: users list array, clientContext object,
    /// userApproval String and status String. In case of an error will return NSError.
    @objc public static func authenticate(_ authCode: String, completionHandler: @escaping (_ authenticationObject: PingOneSDK.AuthenticationObject?, _ error: NSError?) -> Void)

    /// Get one time passcode
    ///
    /// Requests the SDK to provide a one time passcode for authentication.
    ///
    /// - Returns: a completionHandler that contains OneTimePasscodeInfo object.
    /// It will contain all the data of the one time passcode if available.
    /// In case of an error will return NSError.
    @objc public static func getOneTimePasscode(_ completionHandler: @escaping (_ passcodeInfo: PingOneSDK.OneTimePasscodeInfo?, _ error: Error?) -> Void)

    /// Process remote notification action
    ///
    ///  Tells PingOne server to perform the custom action specified by a remote notification. Should be within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///
    /// - Parameters:
    ///   - identifier: The `String` is the response.actionIdentifier received within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///   - userInfo: The `[AnyHashable : Any]` received within userNotificationCenter(_:didReceive:withCompletionHandler:) method.
    ///
    /// - Returns: a completionHandler will return NSError in case of an error.
    /// NotificationObject will be returned in case an action is required.
    @available(*, deprecated, message: "please use processRemoteNotificationAction(identifier:authenticationMethod:userInfo) instead")
    @objc public static func processRemoteNotificationAction(_ identifier: String, forRemoteNotification userInfo: [AnyHashable : Any], completionHandler: @escaping (_ notificationObject: PingOneSDK.NotificationObject?, _ error: NSError?) -> Void)

    /// Remove PingOne local data
    ///
    /// One-sided remove data method for development use only.
    ///
    /// > Warning: Using this method will remove the trusted connection between the iOS SDK and the PingOne server
    /// server in a one sided manner, where only the iOS side will be removed.
    /// This method should not be used when logging out of your account.
    /// This method should only be used in development.
    @objc public static func removePingOneLocalData()

    /// The developer can decide if the device will stay paired after app reinstallation. This method should only be called once.
    ///
    /// - Parameter stayPaired: The `Bool` value. `true` to stay paired. Defaults to `false`.
    @objc public static func setDevicePairedAfterReinstall(_ stayPaired: Bool)

    /// Send logs
    ///
    /// This method will send logs to the PingOne server.
    ///
    /// - Returns: will return NSError in case of an error.
    /// A supportId will be returned when the logs have been sent successfully.
    ///
    /// > Tip: The supportId can then be forwarded to the Ping Identity support team to review the logs.
    @objc public static func sendLogs(completionHandler: @escaping (_ supportId: String?, _ error: NSError?) -> Void)
}

Pairing object error codes

The pairing object always returns both a push authenticator status and an OTP status. If the status is FAILED, a Push authenticator error code and message or an OTP error code and message is returned with the status.

Push authenticator error codes

Error code Description
Any error string returned from APNS, for example Unregistered See more error codes and descriptions in Handling notification responses from APNs in the Apple developer portal.
MissingPushCredentials There are no push credentials on the PingOne server.
MissingDeviceToken There is no device token on the PingOne server.
PushDisabled The push was disabled via the native SDK API.

OTP error codes

Error code Description
OneTimePasscodeRetriesExceeded On device pairing, the OTP can be verified within 3 attempts.
On checkOTP, the OTP can be verified within 3 attempts in every 15 minutes.
InvalidOneTimePasscode The user entered an invalid OTP.
OneTimePasscodeExpired On device pairing, the OTP can be verified within 15 minutes.
On checkOTP, there are no limitations except for OneTimePasscodeRetriesExceeded.
UnSynchronizedClock The native clock not synchronized with the generated secret.

PingOne MFA Native SDK API error codes

Error Status Description
10000 internalError Internal Error.
10001 deviceTokenIsMissing Device token was missing and is required to complete this action.
10002 unrecognizedRemoteNotification Remote notification isn't from PingOne.
10003 serverError There was a server error.
10004 noConnectivity There was a problem with the network.
10005 pairingKey There was a problem with the pairing key.
10006 bundleId There was a problem with the bundle ID.
10007 pairingKeyDataCenterMismatch Device may be paired in one regional data center only, and is already paired in another regional data center.
10008 deviceIsNotPaired Device is not paired.
10009 pushConfirmationTimeout Client confirmation delay responding to push notification, resulted in timeout.
10010 passcodeNotValid Client failed to verify due to invalid one time passcode sent to the server.
10011 authCodeInvalid Authentication code is not valid.
10012 failedPolicyRequirements Mobile device does not comply with policy requirements.

PingOne Verify Native SDKs

The PingOne Verify Native SDKs facilitate communication with the PingOne ID Verification service, handling incoming messages or requests from the service, and sending user ID verification messages or requests to the ID Verification service. It also manages the user ID and transaction ID information by storing keys and data in the secure enclave on the user device.

PingOne Verify SDK for iOS

The PingOne Verify Native SDK for iOS communicates with the PingOne ID Verification service, handling incoming messages or requests from the service, and sending user ID verification messages or requests to the ID Verification service. It also manages the user ID and transaction ID information by storing keys and data in the secure enclave on the iOS device.

Flows

The PingOne Verify iOS SDK transaction flows look like this:

Verify iOS SDK transaction flow diagram

Installation and configuration

Configuring iOS push messaging

Prerequisites

Ensure you have the following information necessary to set up push notifications for your PingOneVerify application:

  • Key ID
  • Team ID
  • Token .p8 file
  • Bundle ID

See the Apple guide Establishing a Token-Based Connection to APNs for more information.

Setting up iOS push notifications
  1. Sign on to the PingOne admin console, and go to Connections --> Applications --> --> Edit --> Authenticator.

  2. Upload your .p8 token and fill in the Key ID, Team ID and Bundle ID values.

  3. Record the Client ID for the newly created native app. You will need to use this Client ID in the app to enable push notifications.

  4. Save your changes.

See the admin documentation Edit an application - Native for more information.

Getting started

After you've installed the PingOne Verify iOS SDK:

  1. Download the PingOne Verify iOS sample app.

  2. Add the PingOne Verify iOS SDK UI library.

    The UI library supplies the classes and methods needed for your PingOne Verify app.

  3. Integrate your app with PingOne.

    You need to add your app information in PingOne to establish a connection.

  4. Initialize the IDVService.

    This enables you to send and receive requests and messages to and from the ID Verification service.

  5. (Optional) Customize the UI of your PingOne Verify app.

    You can change UI colors, labels, buttons, and notifications as needed for your organization.

  6. Capture a user's driver license data or Capture a user's passport data.

  7. Verify the user's selfie.

  8. Send the user's driver license or passport data and selfie to the PingOne ID Verification service for verification and validation.

  9. Get the status of the user's ID verification process from the PingOne ID Verification service.

  10. Process any notifications sent by the PingOne ID Verification service.

Download the sample app

Prerequisites
  • Xcode 12 or greater

  • iOS 10.1 or greater

  • The sample app uses this fork of the iCarousel library. To download the latest version of the iCarousel library, on your iOS device, go to File --> Swift Packages --> Update to Latest Package Versions.

Set up and clone or download

The sample app can not run on a simulator and works only on a device, because the app requires the camera to capture a selfie and and the related user ID documents. The app also relies on the secure enclave on the device for storing information.

  1. Ensure your Xcode is set up with a provisioning profile and a signing certificate to be able to install the app on a device. See the Apple Xcode document Run an app on a device for more information.

  2. Clone or download the PingOne Verify SDK for iOS sample code to your machine and open the P1VerifyApp.xcodeproj file located under the P1VerifyApp directory.

    You'll find all other XCFramework dependencies required for PingOne Verify in the P1Verifyapp/library directory.

  3. To run the sample app, select the scheme P1VerifyApp --> , and click the Run button.

Add the dependencies needed for your application

The PingOne Verify iOS SDK relies on XCFramework components. You'll need to add these to Xcode for use by your application.

  1. If you haven't done so already, clone or download the PingOne Verify SDK for iOS sample app. You'll find the XCFramework dependencies required for the PingOne Verify iOS SDK in the UI Library/P1VerifyLib.xcframework directory.

  2. Add the dependencies:

    a. Click on your target in the project navigator.

    b. Drag and drop the following components to the section Frameworks, Libraries, and Embedded Content in Xcode:

    • P1Verify.xcframework

    • P1VerifyIDSchema.xcframework

    • CommunicationManager.xcframework

    • CryptoTools.xcframework

    • JOSESwift.xcframework

    • JOSETools.xcframework

    c. You can optionally include the UI Library, by dragging and dropping P1VerifyLib.xcframework located in the P1VerifyApp/libraries/UI Library directory to the same Xcode section.

  3. To integrate P1Verify component in your code, enter:

    import P1Verify
    
  4. To integrate the UI Library in your code, enter:

    import ShoLib
    

Integrate your app with PingOne

You'll need to add your native app information in PingOne to establish the client connection:

  1. Log in to the PingOne admin console.

  2. Go to Connections --> Applications, and click Add Application.

  3. Select the Native App tile, and follow the instructions to add your application information.

See Add an application and Edit an application - Native for more information.

Initialize IdvService

Initializing IdvService creates an IdvService object containing the user ID information. This object is then used in subsequent requests and messages sent to the ID Verification service. See the flow diagram in Flows for more information.

You'll need to:

  • Set the PingOne application ID in your app.

  • Initialize IdvService using the 12-digit code, the QR code, or a retained configuration.

  • Set the uniqueToken.

Setting the PingOne application ID in your app

To associate your application you created in PingOne with your iOS app:

  1. Retrieve your application ID from PingOne.

    a. Sign on to the PingOne admin console.

    b. Go to Connections --> Applications --> --> Profile.

    c. Copy the CLIENT ID value (a UUID similar to 2087a363-f1d5-43f1-8111-8ef0a5efed21).

  2. Add the following keypair to your app’s info.plist file to supply the application ID for the registered app in PingOne:

<key>p1v_app_id</key>
<string><your-client-id></string>
Initializing IdvService

When ID verification is enabled for a user, the user initially will be presented with a 12-digit code and the URL for a QR code. You can initialize an IdvService object using either of these.

If ID verification has previously been successful for the user, the user's ID information and transaction ID will still exist in secure storage on their device. In this case, you can use the retained configuration to initialize IdvService.

Initializing IdvService using the 12-digit code

For example:

IdvService.Builder.initWithNewConfig(for: 12_DIGIT_CODE)
    .setPushNotificationToken(PN_TOKEN)
    .create { (result) in
        switch result {
        case .success(let idvService):
            //Use IDVService
        case .failure(let error):
            //Handle IDVService initialization errors here
        }
    }
Initializing IdvService using the QR code URL

For example:

IdvService.Builder.initWithNewConfigFromQr(for: QR_URL)
    .setPushNotificationToken(PN_TOKEN)
    .create { (result) in
        switch result {
        case .success(let idvService):
            //Use IDVService
        case .failure(let error):
            //Handle IDVService initialization errors here
        }
    }
Initialize IdvService using a retained configuration

For example:

IdvService.Builder.initWithSavedConfig()
    .setPushNotificationToken(PN_TOKEN)
    .create { (result) in
        switch result {
        case .success(let idvService):
            //Use IDVService
        case .failure(let error):
            //Handle IDVService initialization errors here
        }
    }

See the IdvService.Builder or IdvService classes for more information.

Enabling push notifications in your development environment

To enable push notifications in the development environment, add coding similar to:

IdvService.Builder.initWithNewConfig(for: 12_DIGIT_CODE)
    .setPushNotificationToken(PN_TOKEN)
    .setPushSandbox(true) // Default value is false
    .create { (result) in
        switch result {
        case .success(let idvService):
            //Use IDVService
        case .failure(let error):
            //Handle IDVService initialization errors here
        }
    }
(Optional) Registering a NotificationHandler

To handle a verification result or any errors while processing notifications, you can register a NotificationHandler in IdvService.Builder. For example:

IdvService.Builder.initWithNewConfig(for: 12_DIGIT_CODE)
    .setPushNotificationToken(PN_TOKEN)
    .setNotificationHandler(YOUR_NOTIFICATION_HANDLER)
    .create { (result) in
        switch result {
        case .success(let idvService):
            //Use IDVService
        case .failure(let error):
            //Handle IDVService initialization errors here
        }
    }

Customize the UI

You can optionally choose to modify the colors and other resources used in the View Controllers, by overriding assets in your app bundle.

Colors

You can override the following colors to personalize all the views:

lib_bg_color
lib_nav_bar_color
lib_button_gray
lib_cancel_text
lib_white_text
lib_text_color

Overriding the following colors will only affect LiveFaceVerificationViewController:

lib_error_text_color
lib_error_view_bg
lib_progress_color
lib_progress_track_color
Image Assets

You can add new image assets using the following names to override the default images in the library:

button_next
button_next_tapped
button_next_disabled
lib_bg
lib_nav_bar_logo
lib_button_confirm
lib_button_confirm_pressed
lib_button_capture

To override the assets used by LiveFaceVerificationViewController, add image assets with these names:

icon_error
lfv_smile
lfv_straight_face
lfv_capture_selfie_step
lfv_close_left_eye
lfv_close_right_eye
lfv_icon_done

To override assets used by DriverLicenseScannerViewController, add assets with these names:

icon_dl_back
icon_dl
info_icon

To override assets used by PassportScannerViewController, add assets with these names:

icon_passport
info_icon
Localization keys for LiveFaceVerificationViewController

The library will look for a LiveFaceVerification.strings file inside the app bundle and if it can't find one, it'll use the default strings.

"different_face_error" = "Different face in the frame";
"missed_expression_error" = "Whoops, missed it. Please try again.";
"no_face_error" = "No Face Detected";
"multiple_faces_error" = "Multiple faces in the frame";
"camera_permission_rationale" = "Access to the camera is required for scanning your IDs and taking pictures.";
"generic_error" = "An error occurred while performing the requested operation. %@";
"capture_selfie_step_instruction" = "This photo is for your profile and can be shared with others.";
"smile_step_instruction" = "Smile!";
"no_smile_step_instruction" = "No Smile.";
"close_left_eye_step_instruction" = "Close your left eye";
"close_right_eye_step_instruction" = "Close your right eye";
"Live Face Verification" = "Live Face Verification";
"Okay" = "Okay";
"Cancel" = "Cancel";
"Skip" = "Skip";
"Error" = "Error";
Localization Keys for DriverLicenseScannerViewController & PassportScannerViewController

The library will look for an IdScanner.strings file inside the app bundle and if it can't find one, it'll use the default strings.

"license_capture_error" = "An error occurred while capturing your license. Please try again.";
"cannot_find_face_error" = "An error occurred while looking for a face in the captured image. Please try again.";
"cannot_scan_barcode_error" = "An error occurred while scanning the code on your license. Please try again.";
"id_information_captured" = "ID information captured";
"scan_barcode" = "Scan the barcode on the back";
"capture_back" = "Capture the back side";
"no_face_found_error" = "No face found in the picture.\nPlease try again and make sure you don\'t crop out the face after taking the picture.";
"Error" = "Error";
"flip_id_message" = "Now flip your ID to %@.";
"picker_default_instruction" = "Choose an option to continue.";
"select_country_instruction" = "Select the issuing country of your Driver License.";
"Driver License" = "Driver License";
"Driver License %@" = "Driver License %@";
"confirm_capture_instruction" = "Confirm you can see your photo clearly and approve the %@.";
"scan_barcode_instruction" = "Align ID inside the guidelines and scan the barcode on the back of your drivers license.";
"Approve %@" = "Approve %@";
"capture_photo_instruction" = "Align ID inside the guidelines and take a photo of the %@ of your %@.";
"Front" = "Front";
"Scan" = "Scan";
"Back" = "Back";
"capture_passport" = "Capture Passport";
"info_page" = "info page";
"Passport" = "Passport";
"Approve %@" = "Approve %@";
"Re-Take" = "Re-Take";
"Cancel" = "Cancel";
"Skip" = "Skip";

Capture driver license data

To capture a user's driver license data, you'll use DriverLicenseScannerViewController to create the DriverLicense object containing the data.

Driver licenses issued in USA and Canada have the PDF417 code per the AAMVA Standard. The IDScanner class defines a composite protocol which is used by the DriverLicenseScannerViewController to find the barcode, if applicable, and extract metadata from the barcode.

By default, AVCaptureMetadataOutput from the AVFoundation framework is used to detect 1D and 2D barcodes. If you want to use a different barcode detector, you can wrap it in IDScanner and pass it to the Builder.

Ensure that you have added the UI library and other dependencies to your project. See Add the dependencies needed for your application for more information.

Initializing DriverLicenseScannerViewController

Initialize DriverLicenseScannerViewController.Builder with either a DriverLicenseScannerListener object or closure blocks to receive completion events with the result.

Here's an example initializing DriverLicenseScannerViewController:

class YourViewController: UIViewController, DriverLicenseScannerListener {

    override func viewDidAppear() {
        super.viewDidAppear()

        try DriverLicenseScannerViewController.Builder { (isComplete, license) in
            guard isComplete,
                  let driverLicense = license else {
                // Canceled
                return
            }
            
            // Handle DriverLicense here
            
        }.setCheckHasFace(checkHasFace: false) // Defaults to true
        .setSteps(scannerSteps: IDScannerStep.capture_front, IDScannerStep.capture_back)
        // -- OR --
        .setCountry(country: "USA")
        .create().show(parentViewController: self)
    }

}

Because the driver license usually contains a photo ID, you can use setCheckHasFace to indicate whether you want the scanner to confirm that there is at least one face present on the front side of the license. setCheckHasFace is set to true by default.

You can either set the country or pass the scanner steps (scannerSteps) to the Builder. If you set only the country, the scanner will determine the steps based on that. If you set the scanner steps and the country, the scanner steps will always override the country parameter.

The scannerSteps values can be: .capture_front, .capture_back, and .scan_id.

If you start the view controller without passing any scanner steps or country, the UI library will display a country selector to the user, and determine IDScannerSteps based on the selection. You can initialize DriverLicenseScannerViewController using this default behavior. For example:

DriverLicenseScannerViewController.Builder { (isComplete, license) in
            guard isComplete,
                  let driverLicense = license else {
                // Canceled
                return
            }
            
            // Handle DriverLicense here
            
        }.create().show(parentViewController: self)

To get the formatted 'DriverLicense' values, use the method ValueFormatter.getFormattedDriverLicense. For example:

ValueFormatter.getFormattedDriverLicense(from: driverLicenseWithRawData)

The default face detector implements SCFaceDetector, and is a wrapper around the iOS class CIDetector for face, from the iOS Core Image Framework. To use a custom face detector, you can use coding similar to this:

try? DriverLicenseScannerViewController.Builder(listener: self)
    .setFaceDetector(faceDetector: YOUR_CUSTOM_FACE_DETECTOR)
    .create()
    .show(parentViewController: self)

You can also pass your custom barcode scanner wrapped as IDScanner to the Builder. To use a custom bar code scanner, you can use coding similar to this:

try? DriverLicenseScannerViewController.Builder(listener: self)
    .setIdScanner(idScanner: YOUR_CUSTOM_ID_SCANNER)
    .create()
    .show(parentViewController: self)

Getting the resulting DriverLicense object

Use the onDriverLicenseCaptured method to get the DriverLicense object returned by the scanner. For example:

func onDriverLicenseCaptured(license: DriverLicense)

You can also use the boolean hasData to determine whether any metadata is recorded. For example:

func onDriverLicenseCaptured(license: DriverLicense) {
    if let frontImage = license.frontImage,
       let backImage = license.backImage
        // Handle ID images here
    }

    if (license.hasData) {
       // Handle ID Data here
    }

}

To get the formatted 'DriverLicense' values, use the method ValueFormatter.getFormattedDriverLicense method. For example:

ValueFormatter.getFormattedDriverLicense(from: driverLicenseWithRawData)

Capture passport data

Use PassportScannerViewController to capture the info page of an international passport, and return the resulting Passport object.

Initializing PassportScannerViewController

Initialize PassportScannerViewController.Builder with a completion block to receive the completion events with the result.

class YourViewController: UIViewController {

    override func viewDidAppear() {
        super.viewDidAppear()

        PassportScannerViewController.Builder(onComplete: { (isComplete, passport) in
            //Completion Block  
            if (isComplete) {
                //Handle Passport here
            } else {
                //ViewController cancelled 
            } 
        })
        .setCheckHasFace(checkHasFace: true)
        .create()
        .show(parentViewController: self)
    }

}

Because a passport always contains a photo ID, you can set setCheckHasFace to indicate whether you want the scanner to confirm that there is at least one face present on the info page of the passport. setCheckHasFace is set to true by default.

The Passport object returned

PassportScannerViewController returns a Passport object containing the image of the info page for the scanned passport.

Currently, the scanner captures only the info page of the passport and doesn't parse any metadata from the photo. The default values for all metadata properties is an empty string.

Selfie capture on iOS

Selfie Capture captures the user's self image (selfie) for facial verification or any other purposes after verifying the user’s physical presence in front of the device camera.

The capture flow supports verification for two pairs of expressions:

  • Open and close eyes
  • Smiling and straight face

Ensure that you have added the UI library and other dependencies to your project. See Add the dependencies needed for your application for more information.

Using SelfieCaptureViewController

Initialize the SelfieCaptureViewController.Builder with a completion block to receive the completion events with result. For example:

class YourViewController: UIViewController {
    override func viewDidAppear() {
        super.viewDidAppear()
        do {
          try SelfieCaptureViewController.Builder() 
             { (isComplete, selfie) in
                if (isComplete) {
                  let selfieImage: UIImage?  = selfie?.getSelfie()
                } else {
                   //Cancel action
                }
             }
              .performExpressionCheck(true) 
                 // set to false to skip expression check
              .start(parentViewController: self)
        } catch {
          // Handle errors thrown by builder here
        }
    }
}

Configurable UI components

To modify the color scheme and other assets in the viewController, you can override the following resources in the app.

assets/images

  • lib_nav_bar_logo (top navigation bar logo)
  • lib_button_capture (capture button image)
  • button_next (next button image for state .normal)
  • button_next_disabled (next button image for state .disabled)
  • button_next_tapped (next button image for state .highlighted)

assets/colors

  • lib_bg_color (background color)
  • lib_nav_bar_color (top navigation bar background color)
  • lib_error_view_bg (error bar background color)
  • lib_error_text_color (error bar text color)
  • lib_white_text (next button text color)
  • lib_button_gray (retake button text color)
  • lib_cancel_text (cancel button text color)

Send user data for verification

When you've captured the user's driver license or passport, as well as the user's selfie, send the resulting DriverLicense or Passport objects, and the Selfie object to the ID Verification service. For example:

idvService.submitDataForValidation(data: array_of_idCardSubclass_objects, onComplete: { (result) in
    switch result {
    case .success(let verifyStatus):
        //Handle status here
    case .failure(let error):
        //Handle errors here
    }
})

Get the user ID verification status

Checking the verification status

You can check the user ID verification status at any time after sending the user data to the ID Verification service. For example:

idvService.checkVerificationStatus()

You'll receive the result in the registered NotificationHandler callback. For example:

public func handleResult(_ verificationResult: VerificationResult) {
    //Handle VerificationResult here
    let verifyStatus: VerifyStatus = verificationResult.getValidationStatus()
    //For a successful verification,
    let claims: [Claim]? = verificationResult.getValidationClaims()
    let userData: UserData? = verificationResult.getUserData()
    //For a failed verification,
    let errors: [ProviderError]? = verificationResult.getValidationErrors()
}

Process notifications from the ID Verification service

Handle any notification messages sent by the ID Verification service. For example:

idvService.processNotification(userInfo: notification_user_info, onComplete: { (result) in
    switch result {
    case .success(let validationResult):
        //Handle validation result here
    case .failure(let error):
        //Handle errors here
    }
})

See processNotification in IdvService for more information.

Class reference

IdCard subclasses

The IdCard subclasses define the currently accepted forms of ID information that can be submitted for a user. The currently available subclasses are:

  • DriverLicense

    U.S and most international driver licenses.

  • Passport

    International passports (2-line MRZ).

  • Selfie

    Live face photo from the user's device. Must be submitted with either DriverLicense or Passport data.

DriverLicense

The DriverLicense subclass defines the user ID information for the U.S and most international driver licenses.

The property values are in Raw format as read from the barcode.

DriverLicense class

The 'DriverLicense' object looks like this:

open class DriverLicense {

    public var cardId: String! //Unique ID to identify the card
    public var id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height: Int!
    internal var firstName, middleName, lastName, gender, addressStreet, addressCity, 
                 addressState, addressZip, country, issueDate, expirationDate, birthDate, 
                 idNumber, weight, height, hairColor, eyeColor: String!
    public var frontImage, backImage: UIImage!

    // You can use getters/setters for the properties to get/set the values. 
    // Values for id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height
    // can only be set internally by the library by calling `setFrontImage`

    // This methods set the property frontImage and throws an error if it cannot find a face  
    // on the image. It also sets the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height
    public func setFrontImage(_ value: UIImage) throws 
    public func getFrontImage() -> UIImage

    // Translates and returns the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height to a CGRect
    public func getFaceCoordinates() -> CGRect

    public func setBackImage(_ value: UIImage)
    public func getBackImage() -> UIImage

    public func setFirstName(_ value: String)
    public func getFirstName() -> String
    public func setMiddleName(_ value: String)
    public func getMiddleName() -> String
    public func setLastName(_ value: String)
    public func getLastName() -> String
    public func setGender(_ value: String)
    public func getGender() -> String
    public func setAddressStreet(_ value: String)
    public func getAddressStreet() -> String
    public func setAddressCity(_ value: String)
    public func getAddressCity() -> String
    public func setAddressState(_ value: String)
    public func getAddressState() -> String
    public func setAddressZip(_ value: String)
    public func getAddressZip() -> String
    public func setCountry(_ value: String)
    public func getCountry() -> String
    public func setIssueDate(_ value: String)
    public func getIssueDate() -> String
    public func setExpirationDate(_ value: String)
    public func getExpirationDate() -> String
    public func setBirthDate(_ value: String)
    public func getBirthDate() -> String
    public func setIdNumber(_ value: String)
    public func getIdNumber() -> String
    public func setWeight(_ value: String)
    public func getWeight() -> String
    public func setHeight(_ value: String)
    public func getHeight() -> String
    public func setHairColor(_ value: String)
    public func getHairColor() -> String
    public func setEyeColor(_ value: String)
    public func getEyeColor() -> String

}
Data model
Property Type Description
id_face_coordinate_x Int The x-axis coordinate of the user's driver license photo.
id_face_coordinate_y Int The y-axis coordinate of the user's driver license photo.
id_face_width Int The width of the user's driver license photo.
id_face_height Int The height of the user's driver license photo.
firstName String The user's first name as shown on the driver license.
middleName String The user's middle name as shown on the driver license.
lastName String The user's last name as shown on the driver license.
gender String The user's gender as shown on the driver license.
addressStreet String The user's street address as shown on the driver license.
addressCity String The user's city of residence as shown on the driver license.
addressState String The user's state or province of residence as shown on the driver license.
addressZip String The user's postal code as shown on the driver license.
country String The user's country of residence as shown on the driver license.
issueDate String The issue date of the user's driver license as shown on the driver license.
expirationDate String The expiration date of the user's driver license as shown on the driver license.
birthDate String The user's date of birth as shown on the driver license.
idNumber String The user's driver license ID number as shown on the driver license.
weight String The user's weight as shown on the driver license.
height String The user's height as shown on the driver license.
hairColor String The user's hair color as shown on the driver license.
eyeColor String The user's eye color as shown on the driver license.
frontImage UIImage The complete front image of the user's driver license.
backImage UIImage The complete back image of the user's driver license.
Passport

The Passport subclass defines the user ID information for all international passports (2-line MRZ).

Passport class

The Passport class looks like this:

open class Passport {

    public var cardId: String! //Unique ID to identify the card
    public var id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height: Int!
    internal var firstName, middleName, lastName, gender, country, nationality, expirationDate,
                 birthDate, idNumber, personalNumber: String!
    public var frontImage: UIImage!

    // You can use getters/setters for the properties to get/set the values. 
    // Values for id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height
    // can only be set internally by the library by calling `setFrontImage`

    // This method sets the property frontImage and throws an error if it cannot find a face  
    // on the image and also sets the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height
    public func setFrontImage(_ value: UIImage) throws 
    public func getFrontImage() -> UIImage

    // Translates and returns the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height to a CGRect
    public func getFaceCoordinates() -> CGRect

    public func setFirstName(_ value: String)
    public func getFirstName() -> String
    public func setMiddleName(_ value: String)
    public func getMiddleName() -> String
    public func setLastName(_ value: String)
    public func getLastName() -> String
    public func setGender(_ value: String)
    public func getGender() -> String
    public func setCountry(_ value: String)
    public func getCountry() -> String
    public func setNationality(_ value: String)
    public func getNationality() -> String
    public func setExpirationDate(_ value: String)
    public func getExpirationDate() -> String
    public func setBirthDate(_ value: String)
    public func getBirthDate() -> String
    public func setIdNumber(_ value: String)
    public func getIdNumber() -> String
    public func setPersonalNumber(_ value: String)
    public func getPersonalNumber() -> String

}
Methods
Method Result type Description
setFrontImage UIImage object Sets the UIImage passed as the value of the frontImage property.
getFrontImage UIImage object Gets the value of the frontImage property.
getFaceCoordinates CGRect object Gets the values of the id_face_coordinate_x, id_face_coordinate_y, id_face_width, and id_face_height properties.
setFirstName String Sets the value of the firstName property.
getFirstName String Gets the value of the firstName property.
setMiddleName String Sets the value of the middleName property.
getMiddleName String Gets the value of the middleName property.
setLastName String Sets the value of the lastName property.
getLastName String Gets the value of the lastName property.
setGender String Sets the value of the gender property.
getGender String Gets the value of the gender property.
setCountry String Sets the value of the country property.
getCountry String Gets the value of the country property.
setNationality String Gets the value of the nationality property.
getNationality String Gets the value of the nationality property.
setExpirationDate String Gets the value of the expirationDate property.
getExpirationDate String Gets the value of the expirationDate property.
setBirthDate String Gets the value of the birthDate property.
getBirthDate String Gets the value of the birthDate property.
setIdNumber String Gets the value of the idNumber property.
getIdNumber String Gets the value of the idNumber property.
setPersonalNumber String Gets the value of the personalNumber property.
getPersonalNumber String Gets the value of the personalNumber property.
Data model
Property Type Description
frontImage UIImage The complete front image (containing the user's photo) of the user's passport.
id_face_coordinate_x Int The x-axis coordinate of the user's passport photo.
id_face_coordinate_y Int The y-axis coordinate of the user's passport photo.
id_face_width Int The width of the user's passport photo.
id_face_height Int The height of the user's passport photo.
firstName String The user's first name as shown on the passport.
middleName String The user's middle name as shown on the passport.
lastName String The user's last name as shown on the passport.
gender String The user's gender as shown on the passport.
country String The user's country of residence as shown on the passport.
nationality String The user's country of birth as shown on the passport.
issueDate String The issue date of the user's passport as shown on the passport.
expirationDate String The expiration date of the user's passport as shown on the passport.
birthDate String The user's date of birth as shown on the passport.
idNumber String The user's passport number (2-line MRZ) as shown on the passport.
personalNumber String The user's personal ID number as shown on the passport.
Selfie

The Selfie subclass defines the live face photo from the user's device. The Selfie object must be submitted with any one of DriverLicense, Passport, or Card objects.

Selfie class

The Selfie class looks like this:

open class Selfie {

    public var cardId: String
    public func setSelfie(_ value: UIImage)
    public func getSelfie() -> UIImage
Methods
Method Result type Description
setSelfie UIImage object Sets the UIImage passed as the value of the selfie property.
getSelfie UIImage object Gets the value of the selfie property (a UIImage object).
Data model
Property Type Description
selfie UIImage The live face photo taken using the user's device.

IdvError

The IdvError class supplies the errors returned from the ID Verification service.

To see the properties, use a constructor to instantiate IdvError, and get the object.

The possible errors returned are all of type String:

Error Description
cannotGetServicePublicKey Thrown by IdvService when it fails to fetch the service JWKS used for secure communication.
cannotGetValidationStatus Failed to get the transaction status from the ID Verification service.
cannotInitializeIdvService Failed to initialize IdvService using either the 12-digit code or the QR code.
cannotSubmitData The request to submit data failed.
cannotUpdatePublicKeys Thrown by IdvService.Builder when the SDK cannot update the client JWKS with the ID Validation service.
cannotValidateIssuedClaim A claim or claims for a successful validation were received, but verification for the claim or claims using the ID Verification service public keys failed.
claimValidationFailed The submitted data packet contains a claim and validation fails for the claim.
failedToLoadSavedConfig Thrown by IdvService.Builder when the SDK cannot find a saved configuration in device storage.
invalidReponseFromIdvService An unexpected response from the ID Verification service was received.

IdvService

Methods
submitDataForVerification

public func submitDataForVerification(data: [IdCard], onComplete: (Result<VerifyStatus, IdvError>) -> Void)

The submitDataForVerification method accepts the user ID information from the app, performs client side verification of the data, and sends the data to the ID Verification service. The SDK retains the transaction ID and service endpoints in encrypted storage on the user device. The completion block (onComplete) notifies the app when the data is shared successfully, or when any errors occur during the process. The updated transaction status is returned in Result.

Internally, the SDK puts the ID information in a secure message to be sent to the ID Verification service. When creating the message, the SDK checks whether any existing claims can be found in secure storage for the given IDs. If the ID information is found, the claims are sent to the ID Verification service with the ID data. This prevents repeated verifications for the same IDs.

checkVerificationStatus

public func checkVerificationStatus(handler: NotificationHandler? = nil)

The checkVerificationStatus method requests the verification status from the ID Verification service, and forwards the status, with any errors or claims, to the callback methods in NotificationHandler. You can register a NotificationHandler using IdvService.Builder or pass one as a function parameter to this method.

setNotificationHandler

public func setNotificationHandler(_handler: NotificationHandler)

Use this method to register a NotificationHandler.

submitDataForVerification

public func submitDataForVerification(data: [IdCard], onComplete: (Result<VerifyStatus, IdvError>) -> Void)

The submitDataForVerification method accepts the user ID information from the app, performs client side validation of the data, and sends the data to the ID Verification service. The SDK retains the transaction ID and service endpoints in encrypted storage on the user device. The completion block (onComplete) notifies the app when the data is shared successfully, or when any errors occur during the process. The updated transaction status is returned in Result.

Internally, the SDK puts the ID information, and a one time security token, in a secure message to be sent to the ID Verification service. When creating the message, the SDK checks whether any existing claims can be found in secure storage for the given IDs. If the ID information is found, the claims are sent to the ID Verification service with the ID data. This prevents repeated validations for the same IDs.

processNotification

public func processNotification(userInfo: [AnyHashable : Any]?, handler: NotificationHandler? = nil) -> Bool

The processNotification method should be called whenever you receive a notification in your app. The method returns true if the notification can be handled by IdvService and it returns false if the service cannot recognize it as a PingOne notification. If the notification indicates that the verification process has completed, the service will call checkVerificationStatus internally, and pass the result to the NotificationHandler callback.

isSandbox

public func isSandbox() -> Bool

The isSandbox method returns a boolean indicating whether IdvService is set for sending push notification in the development (false) or production environment (true).

setPushSandbox

public func setPushSandbox(_ pushSandbox: Bool)

Use this method to enable push notifications in the development environment. By default, the IdvService assumes that the app is running in production and uses the production APNS environment to send notifications.

IdvService.Builder

Methods
initWithNewConfig

public static func initWithNewConfig(for shortcode: String) -> Builder

The initWithNewConfig method initializes IdvService.Builder using the 12-digit code generated by the ID Verification service to create an IdvService object.

initWithNewConfigFromQr

public static func initWithNewConfigFromQr(_ qrUrl: String) -> Builder

The initWithNewConfigFromQr method initializes IdvService.Builder using the content scanned from the QR code generated by the ID Verification service to create an IdvService object.

initWithSavedConfig

public static func initWithSavedConfig() -> Builder

The initWithSavedConfig method loads saved transaction information from secure storage on the user's device and creates an IdvService object using the same configuration.

setNotificationHandler

public func setNotificationHandler(_ handler: NotificationHandler) -> Builder

Use this method to register a NotificationHandler.

setPushNotificationToken

public func setPushNotificationToken(_ pnToken: Data) -> Builder

The setPushNotificationToken method sets the push notification token that will be shared with the ID Verfication service to send a push notification to your app.

setPushSandbox

public func setPushSandbox(_ pushSandbox: Bool) -> Builder

Use this method to enable push notifications in your development environment. By default, the IdvService assumes that the app is running in production and uses the production APNS environment to send notifications.

create

public func create(onComplete: (Result<IdvService, Error>) -> Void)

The create method must be called on a background thread, because it also initializes the secure encrypted storage using the keys in the keychain. This method does the following:

  • Creates a new Entity ID.

  • Initializes encrypted storage in the secure enclave on the user's device using the EC keys.

  • Generates or imports the client RSA key pair used for creating the nested JWT (JWS and JWE).

  • Updates the app JWKs with the Id Verification service.

  • Returns the initialized IdvService asynchronously.

  • Returns an error when:

    • Builder fails to validate the 12-digit code entered by the app.
    • Builder fails to fetch the configuration details using the content in the QR code.
    • No QR metadata or code is passed to the Builder, and it can't find any saved configuration.
    • Builder fails to initialize any of the dependent services manage secure key operations and storage.

VerifyStatus

The VerifyStatus class supplies the status messages that can be returned by the ID Verification service.

The possible status messages are all of type String:

Data model
Status Description
"REQUESTED" The transaction has been initiated, but the user hasn't scanned the QR code or entered the 12-digit code, and hasn't submitted the data.
"IN_PROGRESS" The user has scanned the QR code or entered the 12-digit code, and hasn't submitted the data. The service is preparing to submit the data to the service providers for validation.
"PARTIAL" Some, but not all, of the service providers have responded. The ID Verification service is waiting on a response from the remaining service providers.
"SUCCESS" All of the service providers have responded and the result of the validation is positive from all service providers.
"APPROVED_MANUALLY" An admin has decided to override the result of the user ID verification. The service providers were unable to provide a positive validation, but the admin was able to view the user's physical documents.
"APPROVED_NO_REQUEST" An admin was able to view the users physical documents, and approved the request without the need for third-party validation.
"NOT_REQUIRED" An admin decided that verification isn't required for the user.
"FAIL" All of the service providers have responded and some of the results were not positive. The transactionStatus object will contain error messages.
"DELETED" The request for user ID verification was deleted by a call to the PingOne API DELETE {{apiPath}}/environments/{{envID}}/users/{{userID}}/validationTransactions/{{transactionID}}.

NotificationHandler

Implement the NotificationHandler interface in your class to handle VerificationResult or any errors occurring while processing notifications.

For example:

public protocol NotificationHandler {
    func handleResult(_ verificationResult: VerificationResult)
    func handleError(_ error: IdvError)
}

The VerificationResult object contains the verification status, and either a list of errors (if there's a failed validation) or a list of claims (when there's a successful validation).

The claims are stored by the SDK in encrypted storage. The claims can be used as a proof of ID verification, or the SDK can use the claims in any future verification requests, as long as the ID configuration data hasn’t changed.

ProviderError

The ProviderError class supplies the errors returned from the service provider used by the ID Verification service to validate the user's ID information.

To see the properties, use a constructor to instantiate ProviderError, and get the object.

Methods
Method Result type Description
getProviderId String Returns the unique ID for the validation service provider.
getProviderErrors String array Returns an array of String data containing the errors generated by the validation service provider.

UserData

Methods
public func getFirstName() -> String
public func getLastName() -> String
public func getDateOfBirth() -> String
public func getAddressLine1() -> String
public func getAddressLine2() -> String
public func getCity() -> String
public func getState() -> String
public func getCountry() -> String
public func getPostalCode() -> String
public func getDocumentId() -> String
public func getExpirationDate() -> String
public func getIssueDate() -> String
public func getIdType() -> String
public func getCardType() -> String
public func isEmpty() -> Bool

VerificationResult

The VerificationResult class supplies the result returned from the ID Verification service to the app.

To see the properties, use a constructor to instantiate VerificationResult, and get the object.

Methods
Method Result type Description
getTransactionId String Returns the user's ID verification transaction ID issued by the ID Verification service.
getValidationStatus VerifyStatus object Returns the user's current verification status.
getValidationClaims Claim array Returns the claims for a successful user ID verification.
getValidationErrors ProviderError array Returns an array of ProviderError objects containing the errors generated by the validation provider.
getUserData UserData object Returns the verified information for the user’s ID.

PingOne Verify SDK for Android

The PingOne Verify Native SDK for Android communicates with the PingOne ID Verification service, handling incoming messages or requests from the service, and sending user ID verification messages or requests to the ID Verification service. It also manages the user ID and transaction ID information by storing keys and data in the secure enclave on the Android device.

Flows

The PingOne Verify Android SDK transaction flows look like this:

Verify Android SDK transaction flow diagram

Installation and configuration

Set up Android push messaging

Prerequisites

Ensure you have the following information needed to set up push notifications for your PingOne Verify application using Firebase Cloud Messaging (FCM):

  • Server Key

  • Package name

  • google-services.json configuration file

See the Firebase guide Add Firebase to your Android for more information.

Configure push notifications
  1. Add the google-services.json file to your app and copy the Server Key from your project in the Firebase Console.

  2. Sign on to the PingOne admin console, and go to Connections --> Applications --> --> Edit --> Authenticator --> Enable Android Multi-Factor Authentication.

  3. Paste your Server Key here, and add the Package Name.

  4. Record the Client ID for the newly created native app. You will need to use this Client ID in the app to enable push notifications.

  5. Save your changes.

See the PingOne documentation Edit an application - Native for more information.

Getting started

After you've installed the PingOne Verify Android SDK:

  1. Download the PingOne Verify Android sample app.

  2. Add the dependencies needed for your application.

  3. Integrate your app with PingOne.

    You need to add your app information in PingOne to establish a connection.

  4. Initialize the IDVService.

    This enables you to send and receive requests and messages to and from the ID Verification service.

  5. (Optional) Customize the UI of your PingOne Verify app.

    You can change UI colors, and various resources as needed for your organization.

  6. Configure secure storage

  7. Capture a user's driver license data or Capture a user's passport data.

  8. Verify the user's selfie.

  9. Send the user data to the ID Verification service for verification.

  10. Get the status of the user's ID verification process.

  11. Process any notifications sent by the ID Verification service.

Download the sample app

Prerequisites
  • Android Studio with Gradle

  • Android SDK 24 and up

Get the sample application
  1. Clone or download the PingOne Verify SDK for Android sample app to your machine.

The Sample Code directory contains the Android project that is ready to be built when the prerequisites are satisfied.

  1. To open the sample app as a project in Android Studio, go to File --> New --> Import Project. Choose the Sample Code folder as the project's root folder.

Add the dependencies needed for your application

Download or clone the Github repository for PingOne Verify SDK for Android. You'll find the *.aar and *.jar dependencies required for PingOne Verify in the SDK directory.

Make the following changes to your build.gradle files in order to add the PingOne Verify component to your application:

  1. Open the top (project) level build.gradle file and add these lines to the repositories section under allProjects:
allprojects {
    repositories {
        dirs 'libs'
        ...
    }
}
  1. Create a libs folder if it doesn’t exist under your module, and copy the downloaded *.aar and *.jar dependencies.

  2. Add the following to your module level build.gradle file to include the dependencies in your module:

dependencies {
    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
    ...
}
  1. Because these components are loaded locally, you will also need to include the SDK’s dependencies in the configuration in order to be able to compile and run it.

    a. Add these UI library dependencies:

     ```gradle
     dependencies {
     implementation 'io.fotoapparat.fotoapparat:library:2.7.0'
     implementation 'com.shawnlin:number-picker:2.4.9'
     implementation 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
     implementation 'com.google.android.gms:play-services-vision:20.0.0'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
     implementation 'com.google.firebase:firebase-ml-vision:24.0.3'
     implementation ‘com.google.firebase:firebase-ml-vision-face-model:20.0.1’
     implementation ‘androidx.annotation:annotation:1.1.0’
     implementation ‘androidx.exifinterface:exifinterface:1.3.2’
     implementation ‘org.jetbrains.kotlin:kotlin-stdlib:1.4.20’
     }
     ```
    

    b. Add these PingOne Verify dependencies:

     ```gradle
     dependencies {
     implementation 'androidx.security:security-crypto:1.1.0-alpha03'
     implementation 'androidx.security:security-identity-credential:1.0.0-alpha01'
     implementation 'androidx.biometric:biometric:1.1.0'
     implementation 'androidx.appcompat:appcompat:1.2.0'
     implementation 'com.google.firebase:firebase-messaging:21.0.1'
     implementation 'com.squareup.retrofit2:converter-moshi:2.7.2'
     implementation 'com.squareup.retrofit2:retrofit:2.7.2'
     implementation 'com.squareup.moshi:moshi:1.9.3'
     implementation 'org.bitbucket.b_c:jose4j:0.7.4'
     implementation 'org.slf4j:slf4j-api:1.7.30'
     implementation 'com.squareup.okio:okio:1.16.0'
     implementation 'com.squareup.okhttp3:okhttp:3.14.7'
     }
     ```
    

Integrate your app with PingOne

You'll need to add your native app information in PingOne to establish the client connection:

  1. Log in to the PingOne admin console.

  2. Go to Connections --> Applications, and click Add Application.

  3. Select the Native App tile, and follow the instructions to add your application information.

See Add an application for more information.

  1. Record the Client ID for the newly created app, and set this value as the value of the static variable APPLICATION_ID in IdvHelper.java in the package com.pingidentity.sample.P1VerifyApp.utils. For example:

    private static final String APPLICATION_ID = "<your-client-id>";
    

Initialize IdvService

Initializing IdvService creates an IdvService object containing the user ID information. This object is then used in subsequent requests and messages sent to the ID Verification service. See the flow diagram in Flows for more information.

You'll need to initialize IdvService using the 12-digit code, the QR code, or a retained configuration.

Prerequisites
  • Push Notification Token: Check Configure Android push messaging to setup push notifications using FCM. This depends upon your setting for Configure push notifications in Installation and configuration.

  • PingOne application Id: This is the Client ID for your application on the PingOne Console. To retrieve this value, sign on to the PingOne admin console, and go to Connections → Applications → → Profile and copy the CLIENT ID (this is a UUID, similar to 2087a363-f1d5-43f1-8111-8ef0a5efed21).

Initializing IdvService

When ID verification is enabled for a user, the user initially will be presented with a 12-digit code and the URL for a QR code. You can initialize an IdvService object using either of these.

If ID verification has previously been successful for the user, the user's ID information and transaction ID will still exist in secure storage on their device. In this case, you can use the retained configuration to initialize IdvService.

Initialize IdvService using the 12-digit code

For example:

IdvService.Builder.initWithNewConfigFor(verificationCode)
    .setApplicationId(APPLICATION_ID)
    .setPushNotificationToken(PUSH_TOKEN)
    .create(fragmentActivity, 
    (idvService) -> {
      //Use IdvService
    },
    (error) -> {
      //Handler IdvService initialization error here
    });
Initialize IdvService using the QR code URL

For example:

IdvService.Builder.initWithNewConfigFromQr(qrUrl)
    .setApplicationId(APPLICATION_ID)
    .setPushNotificationToken(PUSH_TOKEN)
    .create(fragmentActivity, 
    (idvService) -> {
      //Use IdvService
    },
    (error) -> {
      //Handler IdvService initialization error here
    });
Initialize IdvService using a retained configuration

For example:

IdvService.Builder.initWithSavedConfig()
    .setApplicationId(APPLICATION_ID) 
    .setPushNotificationToken(PUSH_TOKEN) 
    .create(fragmentActivity, 
    (idvService) -> {
      //Use IdvService
    },
    (error) -> {
      //Handler IdvService initialization error here
    });

See the IdvService.Builder or IdvService classes for more information.

Register NotificationHandler (optional)

To handle the verification result or any errors while processing notifications, you can register a NotificationHandler in IdvService.Builder. For example:

IdvService.Builder.initWithNewConfigFor(verificationCode)
    .setApplicationId(APPLICATION_ID)
    .setPushNotificationToken(PUSH_TOKEN)
    .setNotificationHandler(NOTIFICATION_HANDLER)
    .create(fragmentActivity, 
    (idvService) -> {
      //Use IdvService
    },
    (error) -> {
      //Handler IdvService initialization error here
    });

Customize the UI

You can optionally choose to modify the colors and other resources used in the View Controllers, by overriding assets in your app bundle.

Dimens

You can override the following dimensions to personalize all the views:

lib_text_size_small: 18sp
lib_text_size_medium: 21sp
lib_text_size_large: 24sp
lib_button_text_size: 18sp
Colors

You can override the following colors to personalize all the views:

lib_blue_text_color
lib_text_color
lib_cancel_text_color
lib_progress_bar_color
Drawable

You can override the following drawable assets to personalize all the views:

lib_activity_bg
lib_nav_bar_bg
lib_nav_bar_logo
lib_button_confirm_selector
lib_button_capture
Localization keys for the UI library

You can override the following keys in your strings.xml file for localization:

<string name="button_okay">Okay</string>
<string name="button_retake">Re-Take</string>
<string name="button_confirm">Confirm</string>
<string name="button_approve">Approve</string>
<string name="button_next">Next</string>
<string name="button_settings">Go to Settings</string>
<string name="button_cancel">Cancel</string>
<string name="lfv_different_face_error_message">Different face in the frame</string>
<string name="lfv_restart_error_message">Whoops, missed it. Please try again.</string>
<string name="lfv_no_face_error_message">No Face Detected</string>
<string name="lfv_multiple_faces_error_message">Multiple faces in the frame</string>
<string name="permission_rationale_common_message">The app was unable to proceed. Please update the permissions for your app in device settings to be able to use this feature.</string>
<string name="cannot_save_selfie_error">An error occurred while processing the verified selfie. Please try again.</string>
<string name="cannot_save_picture_error">An error occurred while processing the captured image. Please try again.</string>
<string name="lfv_instruction_text">Align your face with the guidelines.</string>   
<string name="lfv_default_string">Live Face Verification</string> 
<string name="lfv_smile">Smile!</string>
<string name="lfv_straight_face">Show No Expression</string>
<string name="lfv_close_left_eye">Close your left eye</string>
<string name="lfv_close_right_eye">Close your right eye</string>
<string name="dl_step_capture_front_msg">Align ID inside the guidelines and take a photo of the front of your driver license.</string>
<string name="dl_step_capture_front">Driver License Front</string>
<string name="dl_step_approve_front_msg">Confirm you can see your photo clearly and approve the front.</string>
<string name="dl_step_approve_front">Approve Front</string>
<string name="dl_step_scan_msg">Align ID inside the guidelines and scan the barcode on the back of your driver license.</string>
<string name="dl_step_scan">Driver License Scan</string>
<string name="dl_step_capture_back_msg">Align ID inside the guidelines and take a photo of the back of your driver license.</string>
<string name="dl_step_capture_back">Driver License Back</string>
<string name="dl_step_approve_back_msg">Confirm you can see your photo clearly and approve the back.</string>
<string name="dl_step_approve_back">Approve Back</string>
<string name="license_instruction_text">Make sure your license is brightly lit.</string>
<string name="license_scanner_error">An error occurred while scanning the code on your license. Please try again.</string>
<string name="license_select_country_message">Select the issuing country of your Driver License.</string>
<string name="no_face_in_picture">No face found in the picture. Please try again and make sure you don't crop out the face after taking the picture.</string>

Configure secure storage

The PingOne Verify SDK uses EncryptedPreferences to store the entity information and Claims on the device. EncryptedPreferences used for the SDK storage works in conjunction with the device authentication, using either biometrics or device credentials. If biometrics is disabled or no biometrics are enrolled, to be able to use the SDK, the device must have a passcode enabled.

The following methods provide a simple interface to create EncryptedSharedPreferences with or without requiring device authentication. See the Android documentation EncryptedSharedPreferences for more information.

Secure storage without authentication

To use the device's secure storage without authentication, use coding similar to this:

EncryptedPreferences.create(PREFERENCES_FILE_NAME, 
    (sharedPreferences) -> {
      //Use preferences here
    },
    (error) -> {
      //Handle preference initialization error here
    });
Secure storage with authentication

To use the device's secure storage with authentication, you'll need to pass the Fragment or the FragmentActivity that will be used to display the system authentication dialog. For example:

EncryptedPreferences.createAuthenticated(YOUR_FRAGMENT or FRAGMENTACTIVITY, PREFERENCES_FILE_NAME, 
    (sharedPreferences) -> {
      //Use preferences here
    },
    (error) -> {
      //Handle preference initialization error here
    });

Capture driver license data

Driver licenses issued in USA and Canada have the PDF417 code per the AAMVA Standard. The IDScanner class is used by DriveLicenseScannerDialogFragment and DriveLicenseScannerActivity to find the barcode, if applicable, and extract metadata from the barcode.

By default, the library uses Detector from the Native Vision API to detect 1D and 2D barcodes. If you want to use a different barcode detector, you can wrap it in IDScanner and pass it to the Builder in the arguments bundle. Apps can use either DriverLicenseScannerDialogFragment or DriverLicenseScannerActivity, depending on the app architecture and your preference.

Scanning the driver license

To scan the driver license from your app using the default mode:

  • Create a bundle with arguments.

  • Use the scanner from either a FragmentActivity or a Fragment.

Creating a bundle with arguments

Use coding similar to this:

Bundle bundle = new DLSBundle.Builder()
                // --- Optional ---
                .setCaptureFront(captureFront)
                .setCaptureBarcode(captureBarcode)
                .setCaptureBack(captureBack)
                .setCheckHasFace(checkForFace) //Defaults to true
                // ----------------
                .create();

Use the scanner from a FragmentActivity

Use coding similar to this:

void scanDriverLicense() {
    Intent intent = new Intent(this, DriverLicenseScannerActivity.class);
    intent.putExtras(bundle);
    startActivityForResult(intent, DLS_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == DLS_REQUEST_CODE && data != null) {
        if (resultCode == RESULT_OK) {
            final DriverLicense driverLicense = DLSBundle.getDriverLicense(data.getExtras());
            // Use DriverLicense here
        } else if (resultCode == RESULT_CANCELED) {
            // Cancelled
        }
    }
}

Use the scanner from a Fragment

Use coding similar to this:

void scanDriverLicense() {
    DriverLicenseScannerDialogFragment.start(this, bundle, new DriverLicenseScannerListener() {
        @Override
        public void onDriverLicenseCaptured(Bundle bundle) {
            final DriverLicense driverLicense = DriverLicenseScannerActivity.DLSBundle.getDriverLicense(bundle);
            // Use DriverLicense here
        }
  
        @Override
        public void onCanceled() {
                    // Cancelled
        }
    });
}

Capture passport data

Use PassportScannerViewController to capture the info page of an international passport, and return the resulting Passport object.

Initializing PassportScannerViewController

Initialize PassportScannerViewController.Builder with a completion block to receive the completion events with the result.

class YourViewController: UIViewController {

    override func viewDidAppear() {
        super.viewDidAppear()

        PassportScannerViewController.Builder(onComplete: { (isComplete, passport) in
            //Completion Block  
            if (isComplete) {
                //Handle Passport here
            } else {
                //ViewController cancelled 
            } 
        })
        .setCheckHasFace(checkHasFace: true)
        .create()
        .show(parentViewController: self)
    }

}

Because a passport always contains a photo ID, you can set setCheckHasFace to indicate whether you want the scanner to confirm that there is at least one face present on the info page of the passport. setCheckHasFace is set to true by default.

The Passport object returned

PassportScannerViewController returns a Passport object containing the image of the info page for the scanned passport.

Currently, the scanner captures only the info page of the passport and doesn't parse any metadata from the photo. The default values for all metadata properties is an empty string.

The returned Passport object looks like this:

open class Passport {

    public var cardId: String! //Unique ID to identify the card
    public var id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height: Int!
    internal var firstName, middleName, lastName, gender, country, nationality, expirationDate,
                 birthDate, idNumber, personalNumber: String!
    public var frontImage: UIImage!

    // You can use getters/setters for the properties to get/set the values. 
    // Values for id_face_coordinate_x, id_face_coordinate_y, id_face_width, id_face_height
    // can only be set internally by the library by calling `setFrontImage`

    // This method sets the property frontImage and throws an error if it cannot find a face  
    // on the image and also sets the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height
    public func setFrontImage(_ value: UIImage) throws 
    public func getFrontImage() -> UIImage

    // Translates and returns the properties id_face_coordinate_x, id_face_coordinate_y,
    // id_face_width, id_face_height to a CGRect
    public func getFaceCoordinates() -> CGRect

    public func setFirstName(_ value: String)
    public func getFirstName() -> String
    public func setMiddleName(_ value: String)
    public func getMiddleName() -> String
    public func setLastName(_ value: String)
    public func getLastName() -> String
    public func setGender(_ value: String)
    public func getGender() -> String
    public func setCountry(_ value: String)
    public func getCountry() -> String
    public func setNationality(_ value: String)
    public func getNationality() -> String
    public func setExpirationDate(_ value: String)
    public func getExpirationDate() -> String
    public func setBirthDate(_ value: String)
    public func getBirthDate() -> String
    public func setIdNumber(_ value: String)
    public func getIdNumber() -> String
    public func setPersonalNumber(_ value: String)
    public func getPersonalNumber() -> String

}

Selfie capture on Android

Selfie Capture captures the user's self image (selfie) for facial verification or any other purposes after verifying the user’s physical presence in front of the device camera.

The capture flow supports verification for two pairs of expressions:

  • Open and close eyes
  • Smiling and straight face

Using SelfieCaptureDialogFragment

Base Activity must be a fragment activity because this is a support-based Fragment (androidx.fragment.app.Fragment).

public class YourActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.your_activity_layout);

        SelfieCaptureDialogFragment.start(YourActivity.this, new SelfieCaptureListener() {
            @Override
            public void onComplete(@NonNull Bundle bundle) {
                Selfie selfie = LFVBundle.getResultSelfie(bundle);
                //Use selfie here
            }

            @Override
            public void onCancelled() {

            }
        });
    }
}

Configurable UI components

To modify the color scheme, font sizes, and other assets in the fragment or activity, you can override the following resources in the app.

dimens

  • lib_text_size_small: 18sp
  • lib_text_size_medium: 21sp
  • lib_text_size_large: 24sp
  • lib_button_text_size: 18sp

colors

  • lib_instruction_text_color
  • lib_cancel_text_color

drawable

  • lib_activity_bg
  • lib_nav_bar_bg
  • lib_nav_bar_logo
  • lib_button_confirm_selector
  • lib_button_capture

Send user data for verification

When you've captured the user's driver license or passport, as well as the user's selfie, send the resulting DriverLicense or Passport objects, and the Selfie object to the ID Verification service. For example:

idvService.submitDataForValidation(idCardsList, 
    (idvStatus) -> {
      //Handle status here
    },
    (error) -> {
      //Handle error here
    });

Get the user ID verification status

You can check the user ID verification status at any time after sending the user data to the ID Verification service. For example:

idvService.checkVerificationStatus(OPTIONAL_NOTIFICATION_HANDLER);

You'll receive the result in the registered NotificationHandler callback. For example:

public void handleResult(final VerificationResult verificationResult) {
    //Handle VerificationResult here
    final VerifyStatus verifyStatus = verificationResult.getValidationStatus();
    //For a successful verification,
    final Collection<Claim> claims = verificationResult.getValidationClaims();
    final UserData userData = verificationResult.getUserData();
    //For a failed verification,
    final List<ProviderMessages> errors = verificationResult.getProviderMessagesList();
}

Process notifications from the ID Verification service

Handle any notification messages sent by the ID Verification service. For example:

idvService.processNotification(notificationData,
    (verificationResult) -> {
      //Handle ValidationResult here
      VerifyStatus status = verificationResult.getStatus().getStatus();
      //For a successful validation,
      Collection<Claim> claims = verificationResult.getClaims();
      //For a failed validation,
      Collection<ProviderMessages> errors = 
            verificationResult.getStatus().getProviderMessagesList();
    },
    if (!idvService.getValue().processNotification(NOTIFICATION_DATA)) {
      // Notification cannot be handled by the SDK
      // Handle your app notifications here           
    }
    (error) -> {
      //Handle error here
    });

See processNotification in IdvService for more information.

Class Reference

DriverLicense

The DriverLicense class defines the user ID information for the U.S and most international driver licenses.

The property values are in Raw format as read from the barcode.

DriverLicense class

The DriverLicense class looks like this:

public class DriverLicense extends IdCard {

    public void setFrontImage(Bitmap frontImage, Rect faceRect)
    public Bitmap getFrontImage()
    public Rect getFaceCoordinates()

    public void setBackImage(_ value: UIImage)
    public Bitmap getBackImage() -> UIImage

    public void setFirstName(final String value)
    public String getFirstName()
    public void setMiddleName(final String value)
    public String getMiddleName()
    public void setLastName(final String value)
    public String getLastName()
    public void setGender(final String value)
    public String getGender()
    public void setAddressStreet(final String value)
    public String getAddressStreet()
    public void setAddressCity(final String value)
    public String getAddressCity()
    public void setAddressState(final String value)
    public String getAddressState()
    public void setAddressZip(final String value)
    public String getAddressZip()
    public void setCountry(final String value)
    public String getCountry()
    public void setIssueDate(final String value)
    public String getIssueDate()
    public void setExpirationDate(final String value)
    public String getExpirationDate()
    public void setBirthDate(final String value)
    public String getBirthDate()
    public void setIdNumber(final String value)
    public String getIdNumber()
    public void setWeight(final String value)
    public String getWeight()
    public void setHeight(final String value)
    public String getHeight()
    public void setHairColor(final String value)
    public String getHairColor()
    public void setEyeColor(final String value)
    public String getEyeColor()

}
Data model
Property Type Description
id_face_coordinate_x Int The x-axis coordinate of the user's driver license photo.
id_face_coordinate_y Int The y-axis coordinate of the user's driver license photo.
id_face_width Int The width of the user's driver license photo.
id_face_height Int The height of the user's driver license photo.
firstName String The user's first name as shown on the driver license.
middleName String The user's middle name as shown on the driver license.
lastName String The user's last name as shown on the driver license.
gender String The user's gender as shown on the driver license.
addressStreet String The user's street address as shown on the driver license.
addressCity String The user's city of residence as shown on the driver license.
addressState String The user's state or province of residence as shown on the driver license.
addressZip String The user's postal code as shown on the driver license.
country String The user's country of residence as shown on the driver license.
issueDate String The issue date of the user's driver license as shown on the driver license.
expirationDate String The expiration date of the user's driver license as shown on the driver license.
birthDate String The user's date of birth as shown on the driver license.
idNumber String The user's driver license ID number as shown on the driver license.
weight String The user's weight as shown on the driver license.
height String The user's height as shown on the driver license.
hairColor String The user's hair color as shown on the driver license.
eyeColor String The user's eye color as shown on the driver license.
frontImage UIImage The complete front image of the user's driver license.
backImage UIImage The complete back image of the user's driver license.

IdCard

The IdCard class defines the currently accepted forms of ID information that can be submitted for a user. The currently available forms are:

  • DriverLicense

    U.S and most international driver licenses.

  • Passport

    International passports (2-line MRZ).

  • Selfie

    Live face photo from the user's device. Must be submitted with either DriverLicense or Passport data.

IdCard class

The IdCard class looks like this:

public class IdCard implements Parcelable {

    public String getCardType()
    public String getCardId()

}
Methods
Method Result type Description
getCardType String One of the following types of ID information: DriverLicense, Passport, or Selfie.
getCardId String The ID associated with the IdCard type.

IdvError

The IdvError class supplies the errors returned from the ID Verification service.

To see the properties, use a constructor to instantiate IdvError, and get the object.

The possible errors returned are all of type String:

Error Description
CannotGetValidationStatusError Failed to get the transaction status from the ID Verification service. Possible causes include: A claim or claims for a successful validation were received, but verification for the claim or claims using the ID Verification service public keys failed.
IdvServiceInitializationError Failed to initialize IdvService using either the 12-digit code or the QR code. Possible causes include: The SDK cannot find a saved configuration in device storage.
CannotSubmitDataError The request to submit data failed. Possible causes include:
  • Failed to fetch the service JWKS used for secure communication
  • Unable to update the client JWKS with the ID Validation service
  • Validation failed for a claim.
EncryptedStorageInitializationError The SDK failed to initialize encrypted shared preferences for secure storage.
EncryptedStorageInitializationError.AuthenticationFailedError The user failed authentication multiple times per the device policy.
EncryptedStorageInitializationError.RequiredAuthenticationNotAvailableError The user device does not have a passcode lock or any form of biometrics enabled or enrolled.
invalidReponseFromIdvService An unexpected response from the ID Verification service was received.

IdvService

Methods
setUniqueToken

public void setUniqueToken(final String uniqueToken)

The setUniqueToken method enables the user to pass to IdvService the uniqueToken received by the app in push notification. Your app will receive a new uniqueToken in the push notification for each transaction. The SDK persists the uniqueToken for all the calls for a single transaction.

setNotificationHandler

public void setNotificationHandler(final NotificationHandler handler)

Use this method to register a NotificationHandler.

submitDataForVerification

public void submitDataForVerification(final List<IdCard> data, final Consumer<VerifyStatus> resultHandler, final Consumer<Throwable> errorHandler)

The submitDataForVerification method accepts the user ID information from the app, performs client side validation of the data, and sends the data to the ID Verification service. The SDK retains the transaction ID and service endpoints in encrypted storage on the user device. The completion block (onComplete) notifies the app when the data is shared successfully, or when any errors occur during the process. The updated transaction status is returned in Result.

Internally, the SDK puts the ID information in a secure message to be sent to the ID Verification service. When creating the message, the SDK checks whether any existing claims can be found in secure storage for the given IDs. If the ID information is found, the claims are sent to the ID Verification service with the ID data. This prevents repeated validations for the same IDs.

checkVerificationStatus

public void checkVerificationStatus(@Nullable NotificationHandler handler)

The checkVerificationStatus method requests the verification status from the ID Verification service, and forwards the status, with any errors or claims, to the callback methods in NotificationHandler. You can register a NotificationHandler using IdvService.Builder or pass one as a function parameter to this method.

processNotification

public boolean processNotification(@NonNull final Map<String, String> notificationData, @Nullable NotificationHandler handler)

The processNotification method should be called whenever you receive a notification in your app. The method returns true if the notification can be handled by IdvService and it returns false if the service cannot recognize it as a PingOne notification. If the notification indicates that the verification process has completed, the service will call checkVerificationStatus internally, and pass the result to the NotificationHandler callback.

IdvService.Builder

Methods
initWithNewConfig

public static Builder initWithNewConfigFor(final String verificationCode)

The initWithNewConfig method initializes IdvService.Builder using the 12-digit code generated by the ID Verification service to create an IdvService object.

initWithNewConfigFromQr

public static Builder initWithNewConfigFromQr(final String qrUrl)

The initWithNewConfigFromQr method initializes IdvService.Builder using the content scanned from the QR code generated by the ID Verification service to create an IdvService object.

initWithSavedConfig

public static Builder initWithSavedConfig()

The initWithSavedConfig method loads saved transaction information from secure storage on the user's device and creates an IdvService object using the same configuration.

setApplicationId

public Builder setApplicationId(final String applicationId)

The setApplicationId method sets the PingOne application ID that will be shared with the ID Verification service to send a push notification to your app.

setNotificationHandler

public Builder setNotificationHandler(final NotificationHandler handler)

Use this method to register a NotificationHandler.

setPushNotificationToken

public Builder setPushNotificationToken(final String pushToken)

The setPushNotificationToken method sets the push notification token that will be shared with the ID Verfication service to send a push notification to your app.

create

public void create(final Fragment fragment, final Consumer<IdvService> resultHandler, final Consumer<Throwable> errorHandler)

The create method must be called on a background thread, because it also initializes the secure encrypted storage using the keys in the keychain. This method does the following:

  • Creates a new Entity ID.

  • Initializes encrypted shared preferences after authenticating using user’s biometrics/device passcode.

  • Updates the app JWKs with the Id Verification service.

  • Returns the initialized IdvService in the resultHandler.

  • Returns an error when:

    • Builder fails to validate the 12-digit code entered by the app.
    • Builder fails to fetch the configuration details using the content in the QR code.
    • No QR metadata or code is passed to the Builder, and it can't find any saved configuration.
    • Builder fails to initialize any of the dependent services manage secure key operations and storage.

NotificationHandler

Implement the NotificationHandler interface in your class to handle VerificationResult or any errors occurring while processing notifications.

For example:

public interface NotificationHandler {
    void handleResult(final VerificationResult verificationResult);
    void handleError(final IdvError error);
}

The VerificationResult object contains the verification status, and either a list of errors (if there's a failed validation) or a list of claims (when there's a successful validation).

The claims are stored by the SDK in encrypted storage. The claims can be used as a proof of ID verification, or the SDK can use the claims in any future verification requests, as long as the ID configuration data hasn’t changed.

Passport

The Passport class defines the user ID information for all international passports (2-line MRZ).

The DriverLicense class looks like this:

public class DriverLicense extends IdCard {

    public void setFrontImage(Bitmap frontImage, Rect faceRect)
    public Bitmap getFrontImage()
    public Rect getFaceCoordinates()

    public void setBackImage(_ value: UIImage)
    public Bitmap getBackImage() -> UIImage

    public void setFirstName(final String value)
    public String getFirstName()
    public void setMiddleName(final String value)
    public String getMiddleName()
    public void setLastName(final String value)
    public String getLastName()
    public void setGender(final String value)
    public String getGender()
    public void setCountry(final String value)
    public String getCountry()
    public void setNationality(final String value)
    public String getNationality()
    public void setExpirationDate(final String value)
    public String getExpirationDate()
    public void setBirthDate(final String value)
    public String getBirthDate()
    public void setIdNumber(final String value)
    public String getIdNumber()
    public void setPersonalNumber(final String value)
    public String getPersonalNumber()
}
Data model
Property Type Description
frontImage UIImage The complete front image (containing the user's photo) of the user's passport.
id_face_coordinate_x Int The x-axis coordinate of the user's passport photo.
id_face_coordinate_y Int The y-axis coordinate of the user's passport photo.
id_face_width Int The width of the user's passport photo.
id_face_height Int The height of the user's passport photo.
firstName String The user's first name as shown on the passport.
middleName String The user's middle name as shown on the passport.
lastName String The user's last name as shown on the passport.
gender String The user's gender as shown on the passport.
country String The user's country of residence as shown on the passport.
nationality String The user's country of birth as shown on the passport.
issueDate String The issue date of the user's passport as shown on the passport.
expirationDate String The expiration date of the user's passport as shown on the passport.
birthDate String The user's date of birth as shown on the passport.
idNumber String The user's passport number (2-line MRZ) as shown on the passport.
personalNumber String The user's personal ID number as shown on the passport.

ProviderMessages

The ProviderMessages class supplies the errors returned from the service provider used by the ID Verification service to verify the user's ID information.

To see the properties, use a constructor to instantiate ProviderMessages, and get the object.

ProviderError class

The ProviderMessages class looks like this:

public class ProviderMessages {
    public String getProviderId()
    public Map<String, String> getMessages()
}
Methods
Method Result type Description
getProviderId String Returns the unique ID for the verification service provider.
getMessages Map of Strings Returns a map of String data containing the messages generated by the verification service provider.

Selfie

The Selfie subclass defines the live face photo from the user's device. The Selfie object must be submitted with any one of DriverLicense, Passport, or Card objects.

Selfie class

The Selfie class looks like this:

public class IdCard implements Parcelable {

    public Bitmap getSelfie()
    public void setSelfie(Bitmap selfie)

}
Data model
Property Type Description
selfie Bitmap The live face photo taken using the user's device.

UserData

Methods
public func getFirstName() -> String
public func getLastName() -> String
public func getDateOfBirth() -> String
public func getAddressLine1() -> String
public func getAddressLine2() -> String
public func getCity() -> String
public func getState() -> String
public func getCountry() -> String
public func getPostalCode() -> String
public func getDocumentId() -> String
public func getExpirationDate() -> String
public func getIssueDate() -> String
public func getIdType() -> String
public func getCardType() -> String
public func isEmpty() -> Bool

VerificationResult

The VerificationResult class supplies the result returned from the ID Verification service to the app.

To see the properties, use a constructor to instantiate VerificationResult, and get the object.

VerificationResult class

The VerificationResult class looks like this:

public class VerificationResult {
    public DetailedTransactionStatus getValidationStatus()
    public Collection<Claim> getValidationClaims()
    public List<ProviderMessages> getProviderMessagesList()
    public UserData getUserData()
}
Methods
Method Result type Description
getValidationStatus VerifyStatus object Returns the user's current verification status.
getValidationClaims Collection of claims Returns the claims for a successful user ID verification.
getProviderMessagesList List of provider messages Returns the errors for a failed user ID verification.
getUserData UserData Returns the verified ID information.

VerifyStatus

The VerifyStatus class supplies the status messages that can be returned by the ID Verification service. The possible status messages are all of type String.

VerifyStatus class

The VerifyStatus class looks like this:

public enum VerifyStatus {
  REQUESTED,
  IN_PROGRESS,
  PARTIAL,
  SUCCESS,
  APPROVED_MANUALLY,
  APPROVED_NO_REQUEST,
  NOT_REQUIRED,
  FAIL
}

Data model

Status Description
"REQUESTED" The transaction has been initiated, but the user hasn't scanned the QR code or entered the 12-digit code, and hasn't submitted the data.
"IN_PROGRESS" The user has scanned the QR code or entered the 12-digit code, and hasn't submitted the data. The service is preparing to submit the data to the service providers for validation.
"PARTIAL" Some, but not all, of the service providers have responded. The ID Verification service is waiting on a response from the remaining service providers.
"SUCCESS" All of the service providers have responded and the result of the validation is positive from all service providers.
"APPROVED_MANUALLY" An admin has decided to override the result of the user ID verification. The service providers were unable to provide a positive validation, but the admin was able to view the user's physical documents.
"APPROVED_NO_REQUEST" An admin was able to view the users physical documents, and approved the request without the need for third-party validation.
"NOT_REQUIRED" An admin decided that verification isn't required for the user.
"FAIL" All of the service providers have responded and some of the results were not positive. The transactionStatus object will contain error messages.
"DELETED" The request for user ID verification was deleted by a call to the PingOne API DELETE {{apiPath}}/environments/{{envID}}/users/{{userID}}/validationTransactions/{{transactionID}}.

PingOne for Individuals Native SDKs

The PingOne for Individuals Native SDK enables native applications, whether on mobile devices or service endpoints, to communicate with one another, securely share information, and receive and issue verifiable claims, all without having to use a centralized service holding the data exchanged.

PingOne for Individuals Server SDK

We supply the Ping wallet app, a native app for iOS and Android devices, as a reference wallet app. Service endpoints, however, must be built and customized by issuers. A common way to build these services is by embedding the PingOne for Individuals Server SDK (Java) in a service. This enables the service to participate in our Personal Identity framework, and interact with compatible wallet apps that implements our SDK interfaces.

Such compatible wallet apps utilizes our PingOne for Individuals Server SDK, just as a service endpoint might, though designed for native mobile applications.

PingOne for Individuals Server SDK overview

PingOne for Individuals Server SDK flow between endpoint web service, PingOne, and a mobile device

Implemented in a service endpoint, the PingOne for Individuals Server SDK enables a service to create a QR code with instructions for a compatible wallet app to scan, and establish communications with the service. The QR code includes a unique shocardID that identifies the service that created the QR Code. You'll use the shocardID to establish encrypted data communication. The QR code can also include other data, such as a request for information. This data is contained in a JSON structure. Each QR code also maintains a unique ss_id, a challenge request created by the server that must be signed by the compatible wallet app to establish a communication session. The server can then index a receiving request using the supplied ss_id.

After the compatible wallet app scans the QR code, it can create a secure envelope on the device that includes the ss_id, the requested information, and the compatible wallet app's own shocardID. This data is then signed by the private key of the mobile device user in the compatible wallet app, and encrypted with the public key of the service. The PingOne for Individuals Server SDK supplies the interfaces to the Personal Identity platform to retrieve the registered public key for any valid shocardID presented.

The ID Routing service receives the secure envelope, although it is unable to view any of the content. However, it does have the shocardID of the recipient, in this case, the service endpoint. The ID Routing service routes the encrypted packet to the service endpoint, and the service uses the PingOne for Individuals Server SDK to decrypt and validate the content of the secure envelope, and its recipient. The service can then process the information it received.

All communication between any two applications is done in a manner similar to this, and remember, an application can be a compatible wallet app or a service endpoint.

Creating verifiable credentials

When a service receives user information from a compatible wallet app, it can store the user’s shocardID for future reference and communication. The service sends messages using the user's shocardID through the PingOne for Individuals Server SDK. The messages are routed using the ID Routing service, and sent to the wallet app using push notifications. The ID Routing service also maintains a mailbox of messages, so even if a push notification is not received by the app (or ignored), when the user returns to the app, the messages can be retrieved and processed.

PingOne for Individuals verifiable credential examples

The service endpoint can create any credential it finds appropriate for the user. Each credential can contain a graphic display in SVG format, along with any set of key-value pairs. For example, a bank may issue a credential to a user that contains the following information:

bankName = “bxFinance”
accountNumber = “123456789”
accountCreateDate = “June 21, 2021”
accountType = “checking”

The service endpoint can then create a certification of this credential data using the PingOne for Individuals Server SDK. Using this process, a verifiable claim is created. In the PingOne for Individuals Server SDK, the key is hashed along with a salt. The value is also hashed with a different salt, and signed with the private key of the service endpoint maintained in the PingOne for Individuals Server SDK. This is repeated for all four key-value pairs in the example above, and placed in a JSON structure. The JSON is also hashed and signed using the same private key. The resulting structure along with the signature is hashed, and then sent to the ID Registration service so it can be registered in a smart contract on the blockchain.

The ID Registration service acts as a proxy to create the smart contract for the user authorized by the user’s private key signing the claim. This smart contract is maintained in the blockchain.

Using the ID Routing service, the PingOne for Individuals Server SDK on the service endpoint then sends back to the user in a secure envelope:

  • The credential (including all the key-value pairs).

  • The corresponding salt values used to hash individual pieces of data.

  • The signed certification claim, along with a pointer to where the smart contract is located. is sent in a secure-envelope back to the user via the ID Routing service.

Using a compatible wallet app on their mobile device, the user can now share with any other application or service this new credential (or any individual key-value pairs), along with the verifiable proof of the information received from the service endpoint.

Code samples

You'll find a current PingOne for Individuals Server SDK code sample at https://github.com/pingidentity/pingone-for-individuals-server-sdk.

Class Reference

ApplicationInstance

The ApplicationInstance class represents a particular instance of either a compatible wallet app or a PingOne for Individuals server application.

The compatible wallet app or PingOne for Individuals server application initially creates a new ApplicationInstance, registers the application with the ID-routing service, and saves the application locally. The application will then always uses the same ApplicationInstance.

The ApplicationInstance class looks like this:

class ApplicationInstance {
  UUID applicationInstanceId;
  JsonWebKeySet jwks;
  String pushToken;
  String osType: String;
  String toJson();
  static ApplicationInstance fromJson(String jsonString);
}

Use the ApplicationInstance constructor to create the object from the serialized string returned by toJson.

Data model
Property Description
applicationInstanceId The UUID for the compatible wallet app or PingOne for Individuals server application.
jwks The JSON Web Key Set (JWKS) the application will use to verify the PingOne for Individuals server Java Web Token (JWT).
pushToken A string used to send push notifications to compatible wallet apps or callback URLs for PingOne for Individuals server applications.
osType A string identifying the operating system of a compatible wallet app or PingOne for Individuals server application.
toJson Serializes the ApplicationInstance object to JSON.

ApplicationInstanceCreator

Use the ApplicationInstanceCreator class to create the initial ApplicationInstance class needed to instantiate the DistributedIDClient class. You need to call ApplicationInstanceCreator only once for the entire lifetime of the instance of the PingOne for Individuals Server SDK. This class follows a Builder pattern exposing methods that return the ApplicationInstanceCreator class. The build method creates an instance of the ApplicationInstance class.

The ApplicationInstanceCreator looks like this:

class ApplicationInstanceCreator {
  ApplicationInstanceCreator withPingBaseUrl(String pingBaseUrl)
  ApplicationInstanceCreator withJsonWebKeySet(JsonWebKeySet jsonWebKeySet)
  ApplicationInstanceCreator withPushToken(String pushToken)
  ApplicationInstanceCreator withCallbackURL(String callbackURL)
  build() -> ApplicationInstance
}
Methods
withPingBaseUrl
void withPingBaseUrl(String pingBaseUrl)

The withPingBaseUrl method sets the URL to use for server application callbacks. This can be:

  • https://api.pingone.com/v1/ (the default)
  • https://api.pingone.ca/v1/
  • https://api.pingone.eu/v1/
  • https://api.pingone.asia/v1/
  • https://api-staging.pingone.com/v1/
  • https://api-test.pingone.com/v1/
withJsonWebKeySet
void withJsonWebKeySet(JsonWebKeySet jsonWebKeySet)

The withJsonWebKeySet method assigns the JWKS the application will use. You can use the JwksGenerator class to create a new key set:

JwksGenerator keySetGenerator = new JwksGenerator();
JsonWebKeySet jsonWebKeySet = keySetGenerator.generate();
withPushToken
void withPushToken(String pushToken)

The withPushToken method assigns the string containing the push notification token. When creating an ApplicationInstance for a mobile application, a push notification token is required. This token is used to send notifications to the client using the Apple Push Notification service (APNS) or the Google Firebase service.

withCallbackURL
void withCallbackURL(String callbackURL)

The withCallbackURL method assigns the string identifying the callback URL to use. When creating an ApplicationInstance object for a server instantiating the DistributedIDClient class, a callback URL is required. This URL is used by the PingOne for Individuals platform to notify the server application when messages are sent to the SDK.

The ID-routing service uses this callback URL if the server application has messages. For example, if the server application has a callback URL of https://example.com/callback?id=123, the ID-routing service will call POST https://example.com/callback?id=123 to deliver messages. In this case, the request body will be empty, and the ID-routing service is expected to call DistributedIdClient.processMessages().

Challenge

Use the Challenge class to correlate separate requests, or to expire a request after a certain time. For example, a new compatible wallet app session is created when a client calls the requestShare method to request another client to share data. When the receiving client sends the data back, the client should send the same Challenge object. The SDK instance on the recipient's end will check the expiration time passed in the challenge (if any), and terminate any further communication on the expired challenge.

The Challenge class looks like this:

class Challenge {
  UUID id;
  Instant expiresAt;
}
Data model
Property Description
id The UUID for the Challenge object.
expiresAt An Instant object containing the UNIX timestamp.

Claim

A Claim represents a set of keys and values that are asserted to be correct by the issuer.

The Claim class looks like this:

class Claim {
  UUID id;
  Instant createDate;
  SaltedData issuer;
  SaltedData subject;
  SaltedData holder;
  SaltedData referenceClaimID;
  Map<SaltedData, SaltedData> claimData;
  String dataJSON;
  String dataSignature;
  String dataHash;
  String partitionId;
}
Data model
Property Description
claimID A string containing the unique ID of the claim locator (assigned by the Id Registration service).
createDate A Instant object containing the timestamp for when the claim was created.
issuer A SaltedData object containing the claim issuer information.
subject A SaltedData object containing the claim subject information.
holder A SaltedData object containing the claim holder information.
referenceClaimID (Optional) A SaltedData object used to reference the claim that provided the original data.
claimData A Map of keys and associated values for the claim as SaltedData objects.
dataJSON A string containing the claim information in JSON format.
dataSignature A string containing the signature for the claim.
dataHash A string containing a hash of the claim information.
partitionId A string containing the partition ID. The ID Registration service can use partitions to shard and distribute data to be co-located. In this case, the ID Registration service will reference the partition using this ID.

ClaimReference

The ClaimReference class is identical to the Claim class, with the exception of the claimData property. Use the ClaimReference class a reference to expire a claim, without having to retain personally-identifiable information (PII) information.

The ClaimReference class looks like this:

class Claim {
  UUID id;
  Instant createDate;
  SaltedData issuer;
  SaltedData subject;
  SaltedData holder;
  SaltedData referenceClaimID;
  String dataJSON;
  String dataSignature;
  String dataHash;
  String partitionId;
}
Data model
Property Description
claimID A string containing the unique ID of the claim locator (assigned by the Id Registration service).
createDate A Instant object containing the timestamp for when the claim was created.
issuer A SaltedData object containing the claim issuer information.
subject A SaltedData object containing the claim subject information.
holder A SaltedData object containing the claim holder information.
referenceClaimID (Optional) A SaltedData object used to reference the claim that provided the original data.
dataJSON A string containing the claim information in JSON format.
dataSignature A string containing the signature for the claim.
dataHash A string containing a hash of the claim information.
partitionId A string containing ?? (what partition?).

DIDException

The DIDException class is the base class for all exceptions thrown for the PingOne for Individuals Native SDK. The SDK returns objects of type DIDError when calling back to the MessageHandler implementation in the server or native application.

The DIDException class looks like this:

class DIDException {
  String getMessage();
  String getLocalizedMessage();
}
Methods
Method Description
getMessage Returns the exception message.
getLocalizedMessage Returns a localized version of the exception message, if available.

DistributedIDClient

The DistributedIDClient class supplies all of the methods and data structures necessary for interacting with the PingOne for Individuals services.

To instantiate the DistributedIDClient class, use either of these calls, depending on whether or not you want to register a message handler at the same time:

new DistributedIdClient.Builder(ApplicationInstance applicationInstance).build();

or

new DistributedIdClient.Builder(ApplicationInstance applicationInstance, MessageHandler messageHandler).build();
Methods
updateApplicationInstance
void updateApplicationInstanceId(String tokenIdOrWebCallbackURL)

The updateApplicationInstance method is typically used by mobile applications to update the device token ID to receive push notifications, or by server applications to update the callback URL.

Parameters
Parameter Description
tokenIdOrWebCallbackURL The push token (for mobile apps), or the callback URL (for server applications).
shareData
void shareData(UUID applicationInstanceId, String message, Challenge challenge, List<Share> shares)

Use the shareData method to send data from one compatible wallet application instance to another trusted compatible wallet application instance.

Parameters
Parameter Description
applicationInstance The UUID for the ApplicationInstance object.
message (Optional) A string containing a brief message to be displayed to the recipient. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
data An array of Share objects. The Share object groups data and associated claims.
createSelfClaim
Claim createSelfClaim(Map<String, String> data)

Use the createSelfClaim method to create a self-asserted Claim for any personally identifiable information (PII) presented by the user. This call is intended to create a proof on behalf of the user that they own the data, have certified it for later verification, and for creation of a claim by a third party.

Parameters
Parameter Description
data A map of key and value pairs for the claim.
createClaim
Claim createClaim(UUID subjectId, String message, Challenge challenge, Map<String, String> data), Claim referenceClaim

Use the createClaim method to create a Claim for a third-party. This method takes an optional, self-asserted Claim as a reference to the applicationInstanceId for which this Claim is being created. The applicationInstanceId receiving the Claim is passed in the subjectId parameter.

Parameters
Parameter Description
subjectId The UUID for the applicationInstanceId object for which the claim is being created.
message (Optional) A string containing a brief message that's passed to the client application. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
data A map of key and value pairs for the claim.
referenceClaim (Optional) A self-asserted Claim object to use as a reference to the applicationInstanceId object for which this claim is being created.
verifyData
void verifyData(List<Share> data)

Use the verifyData method to verify a list of Share objects. This method verifies the data read from the claims passed in the Share objects. Returns a list of DidException objects if the verification fails.

Parameters
Parameter Description
data A list of Share objects containing the claims.
requestShare
void requestShare(recipientId<Entity> toDistributed ID, Challenge challenge, List<String> requestedKeys)

Use the requestShare method to request data from another compatible wallet application instance. You can pass the keys for the application in requestedKeys.

Parameters
Parameter Description
recipientId The UUID for the applicationInstanceId object for which the claim is being created.
message (Optional) A string containing a brief message that's passed to the client application. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
requestedKeys (Optional) A list of strings containing the keys for the client application.
expireClaim
void expireClaim(ClaimReference claimReference, Instant expiryTimestamp, String message, Challenge challenge)

Use the expireClaim method to revoke or expire a claim. This method results in the notification handler being called by the client SDK (the client expireClaimReceived method).

Parameters
Parameter Description
claimReference The Claim object that's to be revoked or expired.
expiryTimestamp An Instant object used to revoke a claim instantly (using a time before the current time).
message (Optional) A string containing a brief message that's passed to the client application. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
processMessages
void processMessages(MessageHandler messageHandler)

or

void processMessages()

The processMessages method processes the messages sent to the client, and if specified, calls the associated MessageHandler instance. When registered mobile applications receive a push notification, call this method to handle the messages associated with the notification type.

Parameters
Parameter Description
messageHandler The MessageHandler object containing the messages to be processed.
handleShare
void handleShare(UUID senderId, String message, Challenge challenge, List<Share> share, List<DidException> errors)

Use the handleShare method when the compatible wallet application instance receives a message containing some data and a claim from another compatible wallet application instance. The received data will be in a secure envelope signed by the sender. The SDK will verify the data and claim in the Share objects received. If the verification fails, a list of errors will be passed in errors.

Parameters
Parameter Description
senderId The UUID for the ApplicationInstance object where the claim was sending the message.
message A string containing a brief message that's passed to the receiving application instance. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
share A List of Share objects containing the data and claims.
errors A List of DidException objects containing the error or errors when verification of the Share objects fails.
handleClaim
void handleClaim(UUID senderId, String message, Challenge challenge, Claim claim, List<DidException> errors)

Use the handleClaim method when the notification contains a message indicating that a client created a claim for the ApplicationInstance. This method is the counterpart to the createClaim method. When the claim is created, a notification is sent to the receiving ApplicationInstance.

The SDK will verify the data in the received Claim using the public key of the issuer. It will also check if the claim has been revoked. If this verification fails, The SDK will pass the list of DidException objects in errors.

Parameters
Parameter Description
senderId The UUID of the ApplicationInstance object where the claim was created.
message A string containing a brief message that's passed to the receiving application instance. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
claim A List of Claim objects containing the data and claims.
errors A List of DidException objects containing the error or errors when verification of the Share objects fails.
handleExpiredClaim
void handleExpiredClaim(UUID senderId, String message, Challenge challenge, ClaimReference claimReference, List<DidException> errors)

Use the handleExpiredClaim method when a notification contains a message indicating that a claim belonging to this application instance was cancelled by the application instance that created the claim. This method is the counterpart to the cancelClaim method.

When a compatible wallet application instance calls the cancelClaim method, a new claim is created, cancelling an existing claim. The SDK will verify the signatures for the cancelled claim and forward the list of DidException objects in errors if verification fails.

Parameters
Parameter Description
senderId The UUID of the ApplicationInstance object where the claim was expired.
message A string containing a brief message indicating a claim was cancelled. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
claimReference A Claim object containing the data and claim.
errors A list of DidException objects containing the error or errors when verification of the Share objects fails.
handleShareRequest
void handleShareRequest(UUID senderId, String message, Challenge challenge, List<String> requestedKeys)

Use the handleShareRequest when the notification contains a message from another compatible wallet application instance requesting the recipient to share data. This method is the counterpart to the requestShare method. When a sender application instance initiates a requestShare call, it converts to a shareRequestReceived call on the recipient’s end.

Parameters
Parameter Description
senderId The UUID for the ApplicationInstance object from which the message to share data is sent.
message (Optional) A string containing a brief message that's passed to the client application. Although there's no specific limit on the size of the message, there is an overall limit of of 10 Mb for the entire payload (message + challenge + data).
challenge (Optional) A Challenge object to create a new session or share data without a session. An existing session can be passed on from any previous call.
requestedKeys (Optional) A list of strings containing the keys for the client application.
void handleException(DIDException error)
Parameters
Parameter Description
error The DIDException object containing the error information.

Use the handleException method when the SDK is unable to retrieve a message or fails to process the retrieved message. The SDK passes the DIDException object to the sending application instance.

SaltedData

The SaltedData class consists of content and an associated “salt” used to make the storage of the content cryptographically secure.

The SaltedData class looks like this:

class SaltedData {
    String content;
    Salt salt;
}
Data model
Property Description
content A string containing the content.
salt The salt (random data) to be added to a hash of the content.

Share

The Share class encapsulates the data and the associated claim to verify the data. You can choose to share only data without any claim. In this case the receiver will not be able to perform any verification of the data provided.

The Share class looks like this:

class Share {
  List<String> keys;
  Claim claim;
}
Data model
Property Description
keys A list of strings containing the key and value pairs for the claim.
claim (Optional) The Claim object to use for verificatin of the data.

PingOne Fraud Native SDKs

The PingOne Fraud Native SDKs collect data relating to users’ interaction with their devices, including touchscreen and sensors data, as well as additional metadata related to the device itself. The data is continuously sent to the Fraud service for further analysis. Currently, native SDKs are available for these clients:

PingOne Fraud SDK for iOS

Version: 4.4.0

Resources

  • Our sample app using the PingOne Fraud for iOS SDK.

Getting Started

To integrate the PingOne Fraud for iOS SDK on your client:

  1. Add the SDK to your app using CocoaPods. If you’re new to CocoaPods, see the official documentation at https://guides.cocoapods.org/using/using-cocoapods for information about creating and using Podfiles.

  2. Open your project’s Podfile and add the following to your app’s target:

    STVersion = 'VERSION_NUMBER' 
    
    pod 'PingOneFraudSDK', :podspec => "https://assets.pingone.com/p1f/ios-sdk/#{STVersion}/p1f-sdk.podspec"
    
  3. Set the SDK version number. From the command line, install the PingOne Fraud pod library in your project:

    pod install
    
  4. Register PingOne Fraud.

    a. Import the PingOne Fraud module:

    import SecuredTouch
    
  5. If you look in your AppDelegate.swift file you'll see that @UIApplicationMain appears directly before class AppDelegate. This is a special attribute that tells the Swift compiler to generate code to launch your application using default settings. You need to delete @UIApplicationMain from that file, and instead create a new Swift file called main.swift in your project, adding the following in this file:

    import Foundation
    import UIKit
    import SecuredTouch
    
    UIApplicationMain(
        CommandLine.argc,
        CommandLine.unsafeArgv,
        NSStringFromClass(STApplication.self),
        NSStringFromClass(AppDelegate.self)
    )
    
  6. Initialize the SDK. You'll typically do this in your app's application:didFinishLaunchingWithOptions: method. For example:

    let initParams = STInitParams(applicationId: "<appId>", andAppSecret: "<appSecret>", andServerHost: "<serverHost>")
    
    initParams.userId = "<currentUserId>"
    initParams.sessionId = "<sessionId>"
    initParams.debugMode = false
    
    let securedTouch = SecuredTouch.initSDK(initParams)
    
    securedTouch?.onSecuredTouchInitialized { (uuid) in
        print("SecuredTouch Initialized \(uuid!)")
    }
    
    securedTouch?.onSecuredTouchError { (uuid, message, code) in
        print("SecuredTouch onError - uuid: \(uuid!), message: \(message ?? ""), code: \(code)")
    }
    
    Parameter Description
    appId The PingOne application ID. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    appSecret The PingOne application secret. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    serverHost The Fraud service host. See the PingOne Fraud admin documentation for instructions on getting this information.
    userId If the user is already identified (signed on) when initializing the Fraud SDK, their user ID should be set here. If the user ID originates from PingOne, use the user ID for the PingOne environment. For all other cases, use a non-personal user identifier.
    sessionId The user's session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier.
    consoleLogEnabled (Optional) Set this to true to enable console logging by the SDK. Defaults to false.
    externalLogsEnabled (Optional) Set this to false to prevent the SDK from sending error logs to an external logging service. Defaults to true.
  7. Set an event listener to get calls on successful SDK initialization, or for when something goes wrong in the SDK.

    Parameter Description
    uuid Unique identifier for the entire lifetime of the SDK. This value stays the same as long as the app’s process is alive. Also available through securedTouch?.getInstanceUUID().
    message The nature of the failure.
    code Internal error code.
  8. Fetch a Fraud token. The SDK generates a token that's used for Fraud BOT detection capabilities. You'll need to share the token with your application backend so a call with the fetched token can be sent from this backend to the Fraud Evaluation API to retrieve the risk assessment for a session.

    To get the token from the Fraud SDK, add a call to:

    securedTouch?.getToken()
    

    An event is fired every time the token changes. For example:

    securedTouch?.onTokenReady {
      print("SecuredTouch Token is ready, \(securedTouch!.getToken()!)")
    }
    

    Don't use your application to store the token, and instead share the token stored by the SDK.

  9. User log in and log out. You can do this differently, depending on the following:

    • When the application receives the user ID, or any other unique identifier of the user (for example, after sign on), add the call:

      securedTouch?.login(<user-id>)
      
    • If you have a session ID, you can use:

      securedTouch?.login(<user-id>, andSessionId: <session-id>)
      
    • If the application loses the unique identifier of the user (for example, after sign off), add a call to:

      securedTouch?.logout()
      
    • If you have a session ID after the user has signed off, use the following call instead of the call above:

      securedTouch?.logout(<session-id>)
      
  10. Add meaningful IDs to input fields and buttons. Meaningful IDs are required to ensure proper identification of the UI element for detection purposes. Here's an example for a meaningful ID that can be added to a Login button:

    iOS Accessibility identifier for login button

  11. Set the session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier. To set the session ID after the application has it, call:

    securedTouch?.setSessionId(<session-id>)
    
  12. Add tags. You can tag specific points in time during a session that will be saved by the Fraud service.

    Option Command
    Add a single tag securedTouch?.addTag(<tag-name>)
    Add a tag with additional information securedTouch?.addTag(<tag-name>, withValue: <tag-value>)
    Add multiple tags securedTouch?.addTag(<tag-name>, withValue: <tag-value>).addTag(<tag-name>)
  13. Flush the buffer. Flushing the buffer forces the SDK to send buffered data to directly to the PingOne Fraud backend platform. Usually the built-in automatic flush should be sufficient, so this function should not be used unless instructed to do so by Ping Identity Professional Services.

    securedTouch?.flush()
    

Useful operations

  • Pause or resume the PingOne Fraud SDK activity, networking, and data collection:

    Option Command Description
    Pause the SDK securedTouch?.pause() Causes the current state and session ID to be saved, and used on resuming SDK operations.
    Resume the SDK securedTouch?.resume() Resumes normal SDK operations.
  • Pause or resume behavioral data collection:

    Option Command Description
    Pause behavioral data collection securedTouch?.pauseBehavioralData() The SDK stops collecting events related to physical interaction (such as, touchscreen, device sensors, and keyboard). All other SDK activity continues as normal.
    Resume behavioral data collection securedTouch?.resumeBehavioralData() Resumes SDK operations related to physical events.
  • Get a unique identifier for the entire lifetime of the SDK: securedTouch?.getInstanceUUID().

Context APIs

PingOne Fraud does not collect email addresses or phone numbers, but their anonymized features instead (such as, email domain, length, phone number, and country code).

Login page

  • On every sign-on attempt, call the loginAttempt method with the login type. For example:

    // For email login. 
    securedTouch?.loginContext.loginAttemptEmail("email@example.com")
    
    // For social login
    securedTouch?.loginContext.loginAttempt(with: .google)
    

    The social provider value can be: facebook, google, apple, twitter, linkedin, or a custom string. For example: securedTouch?.loginContext.loginAttempt(with: "custom_provider").

  • On a successful sign-on attempt, call the accountCreationTime method with the UNIX epoch timestamp (in seconds) of the account creation time. For example: securedTouch?.loginContext.accountCreationTime(EPOCH_TIME_IN_SECONDS).

  • On failed sign-on attempt, call the loginFailed method. For example: securedTouch?.loginContext.loginFailed().

  • When the user starts a forgot password flow, call the forgotPassword method.

Registration page

  • On every registration attempt, call the registrationAttempt method with the registration type. For example:

    // for email registration
    securedTouch?.registrationContext.registrationAttemptEmail("email@example.com")
    
    // for social registration
    securedTouch?.registrationContext.registrationAttempt(with .google)
    

    The social provider value can be: facebook, google, apple, twitter, linkedin, or a custom string. For example: securedTouch?.loginContext.loginAttempt(with: "custom_provider").

  • On a successful registration, call the registrationSuccess method. For example: securedTouch?.registrationContext.registrationSuccess().

  • On a failed registration, call the registrationFailed method. For example: securedTouch?.registrationContext.registrationFailed().

  • When a referral was applied on a certain registration, call the referralApply method.

Account page

  • On every shipping address change, call the shippingAddressChanged method with the new shipping address. For example: securedTouch?.accountContext.shippingAddressChanged("new address").

  • On every account’s email change, call the emailAddressChanged method with the new email address. For example: securedTouch?.accountContext.emailAddressChanged("new_email@example.com")

  • On every notification settings change, call the notificationChanged method with the updated notification state (on/off) and the notification information. For example:

    // the user turned on promotion notifications
    securedTouch?.accountContext.notificationChanged(true, withInfo: "promotions")
    
    // the user turned off new coupons notifications
    securedTouch?.accountContext.notificationChanged(false, withInfo: "coupons")
    
  • When the user tries to delete an account, call the deleteAccount method.

  • When the user tries to see payment methods available in the account, call the paymentMethodDisplay method.

  • When the user clears account history, call the clearHistory method.

  • When the user buys a gift card, shares a gift card, or redeems a gift card, call the useGiftCard method.

Checkout page

  • On every purchase attempt, call purchaseAttempt with the payment method. For example: securedTouch?.checkoutContext.purchaseAttempt(with: .credit_card). The available PaymentMethod values can be: paypal, credit_card, or a custom payment method. For example: securedTouch?.checkoutContext.purchaseAttempt(with: "other payment method"). Only the payment method type is specified. Do not specify any payment identifiers (such as, credit card number).

  • On a successful purchase, call the purchaseSuccess method. For example: securedTouch?.checkoutContext.purchaseSuccess().

  • On a failed purchase, call the purchaseFailed method. For example: securedTouch?.checkoutContext.purchaseFailed().

Product page

  • When a user adds an item to the cart, call the addToCart method. For example: securedTouch?.productContext.addToCart().

  • When a user saves an item for later, or adds an item to their wishlist, call the saveItem method. For example: securedTouch?.productContext.saveItem().

Catalog page

  • When the user views catalog details, call the viewDetail method.

  • When the user follows a channel, call the followChannel method.

  • When the user unfollows a channel, call the unfollowChannel method.

Challenge page

  • When the application challenges the user, call the challengeInvoked method with the challenge type. Some examples: securedTouch?.challengeContext.challengeInvoked(with: "security questions") or securedTouch?.challengeContext.challengeInvoked(with: .hide_billing). The available challenge type values are: recaptcha, hide_billing.

  • If the user successfully completes the challenge, call the challengeSuccess method. For example: securedTouch?.challengeContext.challengeSuccess().

  • If the user didn’t pass the challenge, call the challengeFailed method. For example: securedTouch?.challengeContext.challengeFailed().

Cart page

  • When the user clears the cart, call the clearCart method.

  • When the user deletes an item from the cart, call the deleteItem method.

  • When the user goes to the checkout page, call the goToCheckout method.

Orders page

  • When the user opens a dispute, call the openDispute method.

  • When the user views a dispute, call the viewDispute method.

  • When the user views an order’s details, call the viewOrderDetails method.

Chat page

  • When the user adds a message in the chat, call the addMessage method.

Feed page

  • When the user starts a stream, call the startStream method.

  • When the user ends a stream, call the endStream method.

Feedback page

  • When the user reports a fraud, call the reportFraud method.

PingOne Fraud SDK for Android

Version: 4.6.0

Resources

  • Our sample app using the PingOne Fraud for Android SDK.

Getting Started

You'll find a sample app using the PingOne Fraud for Android SDK

To integrate the PingOne Fraud for Android SDK on your client:

  1. Add the mavenCentral repository to your root build.gradle file:

    allprojects {
        repositories {
            mavenCentral()
        }
    }
    
  2. Add the PingOne Fraud for Android SDK to your application dependencies.

  3. Add the following to your application-level build.gradle file:

    implementation "com.pingidentity.pingonefraud:android-sdk:${LATEST_VERSION}"
    
  4. Initialize the SDK. Extend the Application class and add the following inside the onCreate method:

    public class MyApplication extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            // optional
            SecuredTouchSDK.setEventListener(new StatusEventListener() {
                @Override
                public void onError(@NonNull String uuid, @NonNull String message, int code) {
                    Log.i("SecuredTouchSDK", "onError " + message + " code: " + code + " uuid: " + uuid);
                }
    
                @Override
                public void onInitialized(@NonNull String uuid) {
                    Log.i("SecuredTouchSDK ", "onInitialized " + uuid);
                }
            });
    
            STInitParams initParams = new STInitParams(<appId>, <appSecret>, <serverHost>)
                .setUserId(<currentUserId>)
                .setSessionId(<sessionId>)
                .setDebugMode(BuildConfig.DEBUG)
                .setConsoleLogEnabled(BuildConfig.DEBUG);
    
            SecuredTouchSDK.init(this, initParams);
        }
    }
    
    Parameter Description
    appId The PingOne application ID. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    appSecret The PingOne application secret. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    serverHost The Fraud service host. See the PingOne Fraud admin documentation for instructions on getting this information.
    userId If the user is already identified (signed on) when initializing the Fraud SDK, their user ID should be set here. If the user ID originates from PingOne, use the user ID for the PingOne environment. For all other cases, use a non-personal user identifier.
    sessionId The user's session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier.
    consoleLogEnabled (Optional) Set this to true to enable console logging by the SDK. Defaults to false.
    externalLogsEnabled (Optional) Set this to false to prevent the SDK from sending error logs to an external logging service. Defaults to true.
  5. Set an event listener to get calls on successful SDK initialization, or for when something goes wrong in the SDK.

    Parameter Description
    uuid Unique identifier for the entire lifetime of the SDK. This value stays the same as long as the app’s process is alive. Also available through SecuredTouchSDK.getInstanceUUID();.
    message The nature of the failure.
    code Internal error code.
  6. Fetch a Fraud token. The SDK generates a token that's used for Fraud BOT detection capabilities. You'll need to share the token with your application backend so a call with the fetched token can be sent from this backend to the Fraud Evaluation API to retrieve the risk assessment for a session.

    To get the token from the Fraud SDK, add a call to:

    SecuredTouchSDK.getToken();
    

    An event is fired every time the token changes. For example:

    SecuredTouchSDK.setTokenReadyListener(new TokenReadyListener() {
      @Override
      public void onTokenReady() {
        Log.i("com.securedtouch", "token ready: " + SecuredTouchSDK.getToken());
      }
    });
    

    Don't use your application to store the token, and instead share the token stored by the SDK.

  7. User log in and log out. You can do this differently, depending on the following:

    • When the application receives the user ID, or any other unique identifier of the user (for example, after sign on), add the call:

      SecuredTouchSDK.login(<user-id>);
      
    • If you have a session ID, you can use:

      SecuredTouchSDK.login(<user-id>, <session-id>);
      
    • If the application loses the unique identifier of the user (for example, after sign off), add a call to:

      SecuredTouchSDK.logout();
      
    • On sign off, the Fraud SDK clears the session ID by default. If you have a session ID after the user has signed off, use the following call instead of the call above:

      SecuredTouchSDK.logout(<session-id>);
      
  8. Set the session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier. To set the session ID after the application has it, call:

    SecuredTouchSDK.setSessionId(<session-id>);
    
  9. Add meaningful IDs to input fields and buttons. Meaningful IDs are required to ensure proper identification of the UI element for detection purposes. Here's an example for a meaningful ID that can be added to a Login button:

    android:id="@+id/button_login"
    
  10. Add tags. You can tag specific points in time during a session that will be saved by the Fraud service. Tags can be added remotely by the PingOne Fraud platform (using CSS selectors), or programmatically using JavaScript or HTML attributes.

    • To add a single tag, use:

      SecuredTouchSDK.addTag(<tag-name>);
      
    • To add a tag with additional information, use:

      SecuredTouchSDK.addTag(<tag-name>, <value>);
      
    • To add multiple tags:

      SecuredTouchSDK.addTag(<tag-name>, <value>). addTag(<tag-name>);
      
  11. Flush the buffer. Flushing the buffer forces the SDK to send buffered data to directly to the PingOne Fraud backend platform. Usually the built-in automatic flush should be sufficient, so this function should not be used unless instructed to do so by Ping Identity Professional Services.

    SecuredTouchSDK.flush();
    

Useful operations

  • Pause or resume the PingOne Fraud SDK activity, networking, and data collection:

    Option Command Description
    Pause the SDK SecuredTouchSDK.pause(); Causes the current state and session ID to be saved, and used on resuming SDK operations.
    Resume the SDK SecuredTouchSDK.resume(); Resumes normal SDK operations.
  • Pause or resume behavioral data collection:

    Option Command Description
    Pause behavioral data collection SecuredTouch.pauseBehavioralData() The SDK stops collecting events related to physical interaction (such as, touchscreen, device sensors, and keyboard). All other SDK activity continues as normal.
    Resume behavioral data collection SecuredTouch.resumeBehavioralData() Resumes SDK operations related to physical events.
  • Get a unique identifier for the entire lifetime of the SDK: SecuredTouchSDK.getInstanceUUID();.

Context APIs

PingOne Fraud does not collect email addresses or phone numbers, but their anonymized features instead (such as, email domain, length, phone number, and country code).

Login page

  • On every sign-on attempt, call the loginAttempt method with the login type. For example:

    // For email login. This doesn't collect the email address itself, only anonymized features (such as, length, and email domain).
    SecuredTouchSDK.LOGIN.loginAttemptEmail("email@example.com");
    
    // For social login
    SecuredTouchSDK.LOGIN.loginAttempt(SocialType.GOOGLE);
    

    The SocialType value can be: FACEBOOK, GOOGLE, APPLE, TWITTER, LINKEDIN, or a custom string. For example: SecuredTouchSDK.LOGIN.loginAttempt("custom_provider");.

  • On a successful sign-on attempt, call the accountCreationTime method with the UNIX epoch timestamp (in seconds) of the account creation time. For example: SecuredTouchSDK.LOGIN.accountCreationTime(EPOCH_TIME_IN_SECONDS);.

  • On failed sign-on attempt, call the loginFailed method. For example: SecuredTouchSDK.LOGIN.loginFailed();.

  • When the user starts a forgot password flow, call the forgotPassword method.

Registration page

  • On every registration attempt, call the registrationAttempt method with the registration type. For example:

    // for email registration
    SecuredTouchSDK.REGISTRATION.registrationAttemptEmail("email@example.com");
    
    // for social registration
    SecuredTouchSDK.REGISTRATION.registrationAttempt(SocialType.GOOGLE);
    

    The SocialType value can be: FACEBOOK, GOOGLE, APPLE, TWITTER, LINKEDIN, or a custom string. For example: SecuredTouchSDK.REGISTRATION.registrationAttempt("custom_provider");.

  • On a successful registration, call the registrationSuccess method. For example: SecuredTouchSDK.REGISTRATION.registrationSuccess();.

  • On a failed registration, call the registrationFailed method. For example: SecuredTouchSDK.REGISTRATION.registrationFailed();.

  • When a referral was applied on a certain registration, call the referralApply method.

Account page

  • On every shipping address change, call the shippingAddressChanged method with the new shipping address. For example: SecuredTouchSDK.ACCOUNT.shippingAddressChanged("new address");.

  • On every account’s email change, call the emailAddressChanged method with the new email address. For example: SecuredTouchSDK.ACCOUNT.emailAddressChanged("new_email@example.com");

  • On every notification settings change, call the notificationChanged method with the updated notification state (on/off) and the notification information. For example:

    // the user turned on promotion notifications
    SecuredTouchSDK.ACCOUNT.notificationChanged(true, "promotions");
    
    // the user turned off new coupons notifications
    SecuredTouchSDK.ACCOUNT.notificationChanged(false, "coupons");
    
  • When the user tries to delete an account, call the deleteAccount method.

  • When the user tries to see payment methods available in the account, call the paymentMethodDisplay method.

  • When the user clears account history, call the clearHistory method.

  • When the user buys a gift card, shares a gift card, or redeems a gift card, call the useGiftCard method.

Checkout page

  • On every purchase attempt, call purchaseAttempt with the payment method. For example: SecuredTouchSDK.CHECKOUT.purchaseAttempt(PaymentMethod.CREDIT_CARD);. The available PaymentMethod values can be: PAYPAL, CREDIT_CARD, or a custom payment method. For example: SecuredTouchSDK.CHECKOUT.purchaseAttempt("other payment method");. Only the payment method type is specified. Do not specify any payment identifiers (such as, credit card number).

  • On a successful purchase, call the purchaseSuccess method. For example: SecuredTouchSDK.CHECKOUT.purchaseSuccess();.

  • On a failed purchase, call the purchaseFailed method. For example: SecuredTouchSDK.CHECKOUT.purchaseFailed();.

  • On coupon usage, call the applyCoupon method. For example: SecuredTouch.CHECKOUT.applyCoupon();.

Product page

  • When a user adds an item to the cart, call the addToCart method. For example: SecuredTouchSDK.PRODUCT.addToCart();.

  • When a user saves an item for later, or adds an item to their wishlist, call the saveItem method. For example: SecuredTouchSDK.PRODUCT.saveItem();.

Catalog page

  • When the user views catalog details, call the viewDetail method.

  • When the user follows a channel, call the followChannel method.

  • When the user unfollows a channel, call the unfollowChannel method.

Challenge page

  • When the application challenges the user, call the challengeInvoked method with the challenge type. Some examples: SecuredTouchSDK.CHALLENGE.challengeInvoked("security questions"); or SecuredTouchSDK.CHALLENGE.challengeInvoked(ChallengeType.HIDE_BILLING);. The available ChallengeType values are: RECAPTCHA, HIDE_BILLING.

  • If the user successfully completes the challenge, call the challengeSuccess method. For example: SecuredTouchSDK.CHALLENGE.challengeSuccess();.

  • If the user didn’t pass the challenge, call the challengeFailed method. For example: SecuredTouchSDK.CHALLENGE.challengeFailed();.

Cart page

  • When the user clears the cart, call the clearCart method.

  • When the user deletes an item from the cart, call the deleteItem method.

  • When the user goes to the checkout page, call the goToCheckout method.

Orders page

  • When the user opens a dispute, call the openDispute method.

  • When the user views a dispute, call the viewDispute method.

  • When the user views an order’s details, call the viewOrderDetails method.

Chat page

  • When the user adds a message in the chat, call the addMessage method.

Feed page

  • When the user starts a stream, call the startStream method.

  • When the user ends a stream, call the endStream method.

Feedback page

  • When the user reports a fraud, call the reportFraud method.

PingOne Fraud SDK for Web

Version: 4.4.0

Resources

  • Our sample app using the PingOne Fraud for Web SDK.

Getting Started

To integrate the PingOne Fraud for Web SDK on your client:

  1. Import the Fraud SDK script. Include the following script section in each relevant page of your app:

    <script id="stPingId"
            src="https://assets.pingone.com/p1f/web-sdk/${LATEST_VERSION}/p1f-sdk.js?appId=<appId>"
            defer>
    </script>
    

    The id="stPingId" setting is mandatory.

  2. Initialize the SDK by adding a listener to the SecuredTouchReadyEvent event:

    function onSecuredTouchReady(callback) {
        if (window['_securedTouchReady']) {
            callback();
        } else {
            document.addEventListener('SecuredTouchReadyEvent', callback);
        }
    }
    onSecuredTouchReady(function () {
        _securedTouch.init({
            url : "https://<securedtouch-backend-host>",
            appId: "<appId>",
            appSecret: "<appSecret>",
            userId: "<currentUserId>",
            sessionId: "<sessionId>",
            isDebugMode: false,
            isSingleDomain: false,
        }).then(function () {
            console.log("SecuredTouchSDK initialized successfully");
        }).catch(function (e) {
            console.error("An error occurred. Please check your init configuration", e);
        });
    });
    
    Parameter Description
    appId The PingOne application ID. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    appSecret The PingOne application secret. See the PingOne Fraud admin documentation for instructions on getting the PingOne Fraud SDK access credentials.
    serverHost The Fraud service host. See the PingOne Fraud admin documentation for instructions on getting this information.
    userId If the user is already identified (signed on) when initializing the Fraud SDK, their user ID should be set here. If the user ID originates from PingOne, use the user ID for the PingOne environment. For all other cases, use a non-personal user identifier.
    sessionId The user's session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier.
    consoleLogEnabled (Optional) Set this to true to enable console logging by the SDK. Defaults to false.
    externalLogsEnabled (Optional) Set this to false to prevent the SDK from sending error logs to an external logging service. Defaults to true.
  3. Fetch a Fraud token. The SDK generates a token that's used for Fraud BOT detection capabilities. You'll need to share the token with your application backend so a call with the fetched token can be sent from this backend to the Fraud Evaluation API to retrieve the risk assessment for a session.

    To get the token from the Fraud SDK, add a call to:

    window['_securedTouchToken']
    

    An event is fired every time the token changes. For example:

    document.addEventListener('SecuredTouchTokenReadyEvent', function () {
      // token = window['_securedTouchToken'];
    });
    

    Don't use your application to store the token, and instead share the token stored by the SDK.

  4. User log in and log out. You can do this differently, depending on the following:

    • When the application receives the user ID, or any other unique identifier of the user (for example, after sign on), add the call:

      _securedTouch.login(<user-id>)
      
    • If you have a session ID, you can use:

      _securedTouch.login(<user-id>, <session-id>)
      
    • If the application loses the unique identifier of the user (for example, after sign off), add a call to:

      _securedTouch.logout()
      
    • On sign off, the Fraud SDK clears the session ID by default. If you have a session ID after the user has signed off, use the following call instead of the call above:

      _securedTouch.logout(<session-id>)
      
  5. Set the session ID. To query the Fraud service for the trust scores through the backend system, the Fraud service and the backend system must have a shared identifier for each session. The session ID usually serves as this shared identifier. To set the session ID after the application has it, call:

    _securedTouch.setSessionId(<session-id>)
    
  6. Add meaningful IDs to input fields and buttons. Meaningful IDs are required to ensure proper identification of the UI element for detection purposes. Here's an example for a meaningful ID that can be added to a username:

    <input id="username">
    
  7. Add tags. You can tag specific points in time during a session that will be saved by the Fraud service. Tags can be added remotely by the PingOne Fraud platform (using CSS selectors), or programmatically using JavaScript or HTML attributes.

    • To add a tag using HTML, add the data-st-tag attribute to an element with the tag key. The tag will be added when the element is clicked. You can also add additional information, by adding the data-st-tag-value attribute. For example:

      <button type="button"
              data-st-tag="placeOrderClick"
              data-st-tag-value="{{payment_type}}">Place Order</button>
      
      <button type="button" 
              data-st-tag="cancelClick">Cancel</button>
      
    • To add a single tag, use:

      _securedTouch.addTag(<tag-name>)
      
    • To add a tag with additional information, use:

      _securedTouch.addTag(<tag-name>, <tag-value>)
      
    • To add multiple tags:

      _securedTouch.addTag(<tag-name>, <tag-value>).addTag(<tag-name>)
      
  8. Flush the buffer. Flushing the buffer forces the SDK to send buffered data to directly to the PingOne Fraud backend platform. Usually the built-in automatic flush should be sufficient, so this function should not be used unless instructed to do so by Ping Identity Professional Services.

    _securedTouch.flush()
    

Useful operations

  • Pause or resume the PingOne Fraud SDK activity, networking, and data collection:

    Option Command Description
    Pause the SDK _securedTouch.pause() Causes the current state and session ID to be saved, and used on resuming SDK operations.
    Resume the SDK _securedTouch.resume() Resumes normal SDK operations.
  • Pause or resume behavioral data collection:

    Option Command Description
    Pause behavioral data collection _securedTouch.pauseBehavioralData() The SDK stops collecting events related to physical interaction (such as, touchscreen, device sensors, and keyboard). All other SDK activity continues as normal.
    Resume behavioral data collection _securedTouch.resumeBehavioralData() Resumes SDK operations related to physical events.
  • Collecting mouse and gestures data:

    Mouse (desktop) and touchscreen gestures (mobile devices) are collected by default for the entire DOM (Document Object Model).

Context APIs

PingOne Fraud does not collect email addresses or phone numbers, but their anonymized features instead (such as, email domain, length, phone number, and country code).

Collect user information

When collecting user information, add data-st-field entries for username and password:

<input type="email" id="username" placeholder="..." data-st-field="username"/>
<input type="password" id="password" data-st-field="password"/>

For the "input type=" entries, use the input type used for the user ID.

Login page

  • On every sign-on attempt, call the loginAttempt method with the login type. For example:

    // For email login. This doesn't collect the email address itself, only anonymized features (such as, length, and email domain).
    _securedTouch.LOGIN.loginAttemptEmail("email@example.com");
    
    // For phone number login (countryCode, nationalNumber):
    _securedTouch.LOGIN.loginAttemptPhoneNumber("1", "2025550134");
    
    // For social login
    _securedTouch.LOGIN.loginAttempt(_securedTouch.SocialType.GOOGLE);
    

    The SocialType value can be: FACEBOOK, GOOGLE, APPLE, TWITTER, LINKEDIN, or a custom string. For example: _securedTouch.LOGIN.loginAttempt("PROVIDER_NAME");.

  • On a successful sign-on attempt, call the accountCreationTime method with the UNIX epoch timestamp (in seconds) of the account creation time. For example: _securedTouch.LOGIN.accountCreationTime(EPOCH_TIME_IN_SECONDS);.

  • On failed sign-on attempt, call the loginFailed method. For example: _securedTouch.LOGIN.loginFailed();.

  • When the user starts a forgot password flow, call the forgotPassword method.

Registration page

  • On every registration attempt, call the registrationAttempt method with the registration type. For example: