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.

PingOne MFA SDK for Android

Overview

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

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

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

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

PingOne Native SDK sample app

The PingOne 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.

PingOne Native SDK API - Android

PingOne main public class

/**
 * Created by Ping Identity
 * Copyright © 2020 Ping Identity. All rights reserved.
 * Version 1.3.0
 *
 * Main public class that contains APIs
 */
@SuppressWarnings("unused")
@Keep
public class PingOne {

    private final static Logger logger = LoggerFactory.getLogger(PingOne.class);


    /**
     * Pairs the device with the PingOne server using the pairing key
     * @param context   application context
     * @param pairingKey    pairing key as a String
     * @param callback  {@link PingOneSDKCallback} notifies a completion
     **/
    @Deprecated
    public static void pair(Context context, String pairingKey, PingOneSDKCallback callback){
        logger.info("SDK called method=pair");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.executePairingRequest(context, pairingKey, callback);
    }

    /**
     * Pairs the device with the PingOne server using the pairing key
     * @param context   application context
     * @param pairingKey    pairing key as a String
     * @param callback  {@link PingOneSDKPairingCallback} notifies the status of the completion.
     * A wrapper method that contains Array List with authentication methods (One time passcode and Push) status.
     * Documentation for pairing objecterror codes:
     * https://apidocs.pingidentity.com/pingone/native-sdks/v1/api/#androidPairingObjectErrorCodes
     **/
    public static void pair(Context context, String pairingKey, PingOneSDKPairingCallback callback){
        logger.info("SDK called method=pair");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.executePairingRequest(context, pairingKey, 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:  String
     * @param callback:  Will return PingOnePairingObjectCallback.
     * Will contain return PairingObject in case further action is required, such as approving the pairing
     * or error in case of failure.
     */
    public static void processIdToken(String idToken, PingOnePairingObjectCallback callback){
        logger.info("SDK called method=processIdToken");
        PingOneInnerLogic pingOneInnerLogic = new PingOneInnerLogic();
        pingOneInnerLogic.processAuthToken(idToken, callback);
    }


    /**
     * Set device remote notification token.
     * @param context an application context
     * @param fcmRegistrationIdToken the registration token retrieved from FCM
     * @param callback notifies a completion
     */
    public static void setDeviceToken(Context context, String fcmRegistrationIdToken, PingOneSDKCallback callback){
        logger.info("SDK called method=setDeviceToken");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.setDeviceToken(context, fcmRegistrationIdToken, 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 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){
        logger.warn("SDK called deprecated method=processRemoteNotification");
        processRemoteNotification(null, remoteMessage, 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 process completed successfully or PingOneSDKError otherwise
     */
    public static void processRemoteNotification(Context context, RemoteMessage remoteMessage, PingOneNotificationCallback callback){
        logger.info("SDK called method=processRemoteNotification");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.handlePush(context, remoteMessage, callback);
    }

    /**
     * Returns a payload string for any communication needed between the customer native app
     * and the PingOne SDK server.
     */
    public static String generateMobilePayload(Context context){
        logger.info("SDK called method=generateMobilePayload");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        return innerLogic.generateMobilePayload(context);
    }


    /**
     *  Returns all the paired users from the PingOne server.
     * @param context the context of calling application
     * @param callback a callback that will be triggered at completion
     */
    public static void getInfo(Context context, PingOneGetInfoCallback callback){
        logger.info("SDK called method=getInfo");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.getDeviceInfo(context, callback);
    }

    /**
     * Send logs to PingOne server.
     * @param context the context of calling application
     * @param callback a callback that will be triggered at completion
     */
    public static void sendLogs(Context context, PingOneSendLogsCallback callback){
        logger.info("SDK called method=sendLogs");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.sendLogs(context, callback);
    }

    /**
     * Requests the SDK to provide One Time Passcode
     * @param context the context of 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){
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.getOneTimePassCode(context, callback);
    }

    /**
     * Method that will notify the server not to send push messages to this device
     * @param context the context of 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){
        logger.info("SDK called method=allowPushNotifications");
        PingOneInnerLogic innerLogic = new PingOneInnerLogic();
        innerLogic.allowPushNotifications(context, allowPush);
    }

    /**
     * Callback interface that will notify developers about asynchronous task completion
     * {@link PingOneSDKError} object will be null if process completed successfully
     */
    public interface PingOneSDKCallback {
        void onComplete(@Nullable PingOneSDKError error);
    }

    /**
     * Callback interface that will notify developers about asynchronous task completion
     * {@link PingOneSDKError} object will be null if process completed successfully
     * {@link PairingInfo} A wrapper method that contains Array List with authentication methods (One time passcode and Push) status.
     * will be null completed with error.
     */
    public interface PingOneSDKPairingCallback extends PingOneSDKCallback{
        void onComplete(PairingInfo pairingInfo, @Nullable PingOneSDKError error);
    }

    /**
     * Callback interface that will notify developers about processRemoteNotification task completion
     * {@link NotificationObject} will returned if process completed successfully
     * {@link PingOneSDKError} object will be null if process completed successfully
     */
    public interface PingOneNotificationCallback {
        void onComplete(@Nullable NotificationObject notificationObject, @Nullable PingOneSDKError error);
    }

    /**
     * Callback interface that will notify developers about processIdToken task completion
     * {@link PairingObject} will be returned if process completed successfully
     * {@link PingOneSDKError} object will be null if 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.
     * {@link PingOneSDKError} object will be null if 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.
     * {@link PingOneSDKError} object will be null if process completed successfully.
     */
    public interface PingOneSendLogsCallback {
        void onComplete(@Nullable String supportId, @Nullable PingOneSDKError error);
    }

    /**
     * Callback interface that will return One time passcode object.
     * {@link PingOneSDKError} object will be null if process completed successfully.
     * {@link OneTimePasscodeInfo} object will be null if process did not complete successfully.
     */
    public interface PingOneOneTimePasscodeCallback {
        void onComplete(@Nullable OneTimePasscodeInfo oneTimePasscodeInfo, @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);
}

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 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.

PingOne SDK for iOS

Overview

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

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

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

PingOne Native SDK sample app

The PingOne 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.

PingOne Native SDK API - iOS (PingOne.h)

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

//! Project version number for PingOne.
public var PingOneVersionNumber: Double

/// 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 public let type: PingOne.AuthenticatorModel.AuthenticatorType

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

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

    /// Error code in case of authorization method failure
    @objc 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
}

/// 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 public let notificationType: PingOne.NotificationObject.NotificationType

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

    /// Extra parameters are passed to the client with the push object
    @objc 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 (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 (NSError?) -> Void)

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

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

    /// The generated passcode
    @objc 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 public let validUntil: Double

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

    /// The total life span of the passcode since it was created until it expired. The time unit is seconds.
    @objc 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: [PingOne.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 (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 (PingOne.PairingInfo?, NSError?) -> Void)
}

@objc public class PingOne : NSObject {

    /// Type that describes the APNS environment.
    @objc public enum APNSDeviceTokenType : NSInteger {

        /// Production environment.
        case production = 0

        /// Sandbox environment.
        case sandbox
    }

    /// Returns a payload string for any communication needed between the customer native app and the PingOne server.
    @objc public static func generateMobilePayload() throws -> String

    /// Pair device
    ///
    /// - Parameters:
    ///   - pairingKey:         The `String` value
    ///   - completionHandler:  Will return NSError in case of an error.
    @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
    ///
    /// - Parameters:
    ///   - pairingKey:         The `String` value
    ///   - completionHandler: Will return PairingInfo object containing data about pairing resolution, and NSError in case of an error. Documentation for pairing object error codes: https://apidocs.pingidentity.com/pingone/native-sdks/v1/api/#pingone-mobile-sdk-for-ios
    @objc public static func pair(_ pairingKey: String, completion: @escaping (PingOne.PairingInfo?, NSError?) -> Void)

    /// When using OpenID Connect, this is a mandatory post action method which should be executed after receiving an ID token from the PingOne server.
    ///
    /// - Parameters:
    ///   - idToken:         The `String` value
    ///   - completionHandler:  Will return NSError in case of an error. Will return PairingObject in case further action is required, such as approving the pairing.
    @objc public static func processIdToken(_ idToken: String, completionHandler: @escaping (PingOne.PairingObject?, NSError?) -> Void)

    ///  Set device remote notification token. Should be within application:didRegisterForRemoteNotificationsWithDeviceToken:
    ///
    /// - Parameters:
    ///   - deviceToken: The `Data` received within application:didRegisterForRemoteNotificationsWithDeviceToken:
    ///   - type: The `APNSDeviceTokenType` case.
    ///   - completionHandler: Will return NSError in case of an error.
    @objc public static func setDeviceToken(_ deviceToken: Data, type: PingOne.PingOne.APNSDeviceTokenType, completionHandler: @escaping (NSError?) -> Void)

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

    /// Method that will notify the server 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.
    ///
    /// - Parameters:
    ///   - userInfo: The `[AnyHashable : Any]` received in the AppDelegate application(_:didReceiveRemoteNotification:fetchCompletionHandler:) method.
    ///   - completionHandler: 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 (PingOne.NotificationObject?, NSError?) -> Void)

    ///  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 in https://tools.ietf.org/html/rfc8176/#section-2 .
    ///   - completionHandler: Will return 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 (PingOne.NotificationObject?, NSError?) -> Void)

    ///  Returns all the paired users from the PingOne server.
    ///
    /// - Parameters:
    ///   - 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 ([String : Any]?, NSError?) -> Void)

    /// Requests the SDK to provide one time passcode
    /// - Parameter completionHandler: Returns OneTimePasscodeInfo that contains data of the one time passcode if available, or error
    @objc public static func getOneTimePasscode(_ completionHandler: @escaping (PingOne.OneTimePasscodeInfo?, Error?) -> Void)

    ///  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.
    ///   - 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 (PingOne.NotificationObject?, NSError?) -> Void)

    ///  **************** 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 to PingOne server.
    ///
    /// - Parameters:
    ///   - completionHandler:  Will return NSError in case of an error. A supportId will be returned when the logs have been sent successfully. The supportId can then be forwarded to the Ping Identity support team to review the logs.
    @objc public static func sendLogs(completionHandler: @escaping (String?, 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 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.

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:

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've added the UI library to your Podfile or View Controller. 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.

Live face verification

Live face verification captures the user's selfie for facial verification or any other purposes, after verifying the user’s physical presence in front of the device camera. The verification (using LiveFaceVerificationViewController) is returned in a Selfie object.

Ensure that you've added the UI library to your Podfile or View Controller. See Add the dependencies needed for your application for more information.

Initialize LiveFaceVerificationViewController

The LiveFaceVerificationViewController class is a View Controller with configurable resources, a camera implementation, and other UI components that can be directly plugged into your app with a few lines of code.

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

class YourViewController: UIViewController {

    override func viewDidAppear() {
        super.viewDidAppear()
        do {
            try LiveFaceVerificationViewController.Builder { (isComplete, selfie) in
            guard isComplete,
                  let selfie = selfie else {
                // Canceled
                return
            }
            
            // Handle Selfie here
        }
        .setAccuracy(accuracy: .low)
        .setVerificationTime(verificationTime: 2.0)
        .setVerificationSteps(verificationSteps: SCLiveFaceVerificationStep.lfv_smile, SCLiveFaceVerificationStep.lfv_straight_face)
        .create().show(parentViewController: self)
        } catch {
            // Handle errors thrown by Builder here
        }
    }

}
  • verificationSteps can be: .lfv_smile, .lfv_straight_face, lfv_close_left_eye, or lfv_close_right_eye.

  • verificationTime must be greater than 0. The default value is 2.5 seconds.

  • The default value for accuracy is SCLiveFaceVerificationAccuracy.medium. This can also be: SCLiveFaceVerificationAccuracy.low or ``SCLiveFaceVerificationAccuracy.high`.

You can initialize LiveFaceVerificationViewController with the default values like this:

LiveFaceVerificationViewController.Builder { (isComplete, selfie) in
            guard isComplete,
                  let selfie = selfie else {
                // Canceled
                return
            }
            
            // Handle Selfie here
        }
        .create().show(parentViewController: self)

By default, the SDK uses Apple's CIDetector to find faces in the image. If you want to use another face detector, you can implement SCFaceDetector, and pass it to the Builder like this:

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

In the SCFaceDetector implementation, the resulting face objects must be converted to SCFace when extending SCFaceDetector.

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 {{appPath}}/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 iOS SDK transaction flows look like this:

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

}

Live face verification

Live face verification captures the user's selfie for facial verification or any other purposes, after verifying the user’s physical presence in front of the device camera. The verification is returned in a Selfie object.

SCLiveFaceVerifier uses FirebaseVisionFaseDetector by default to find faces in the image. If you prefer to use another face detection library, you can wrap it in SCFaceDetector (extend abstract SCFaceDetector) and pass it to the bundle. In the wrapper, the resulting face objects must be converted to SCFace when extending SCFaceDetector.

Starting live face verification

To start live face verification:

  • Create a bundle with arguments.

  • Use live face verification from either a FragmentActivity or a Fragment.

Creating a bundle with arguments

Use coding similar to this:

Bundle bundle = new LFVBundle.Builder()
                // --- Optional ---
                .setVerificationTimeInMillis(2000)
                .setAccuracy(SCLiveFaceVerificationAccuracy.low)
                .setVerificationSteps(SCLiveFaceVerificationStep.lfv_smile, SCLiveFaceVerificationStep.lfv_straight_face)
                // ----------------
                .create();
  • verificationSteps can be: .lfv_smile, .lfv_straight_face, lfv_close_left_eye, or lfv_close_right_eye.

  • verificationTime must be greater than 0. The default value is 2.5 seconds.

  • The default value for accuracy is SCLiveFaceVerificationAccuracy.medium. This can also be: SCLiveFaceVerificationAccuracy.low or ``SCLiveFaceVerificationAccuracy.high`. Using high accuracy may consume more resources and affect the speed of detection.

Using live face verification from a FragmentActivity

Use coding similar to this:

void captureSelfie() {
    Intent intent = new Intent(this, LiveFaceVerificationActivity.class);
    intent.putExtras(bundle);
    startActivityForResult(intent, LFV_REQUEST_CODE);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == LFV_REQUEST_CODE && data != null) {
        if (resultCode == RESULT_OK) {
            final Selfie bitmap = LFVBundle.getResultSelfie(data.getExtras());
            // Use Selfie here
        } else if (resultCode == RESULT_CANCELED) {
            // Cancelled
        }
    }
}
Using live face verification from a Fragment

Use coding similar to this:

void captureSelfie() {
    LiveFaceVerificationDialogFragment.start(this, bundle, new LiveFaceVerificationListener() {
        @Override
        public void onComplete(@NonNull Bundle bundle) {
            final Selfie selfie = LiveFaceVerificationActivity.LFVBundle.getResultSelfie(bundle);
            // Use Selfie here
        }

        @Override
        public void onCancelled() {
            // Cancelled
        }
    });
}

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 {{appPath}}/environments/{{envID}}/users/{{userID}}/validationTransactions/{{transactionID}}.

PingOne ShoCard 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 ShoCard Server SDK

The Ping Identity Personal Identity platform 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.

We supply the ShoCard wallet, a native app for iOS and Android devices. Service endpoints, however, need to be built and customized by providers. A common way to build these services is by embedding the PingOne ShoCard Server SDK (Java) in a service. This enables the service to participate in our Personal Identity framework, and interact with the ShoCard wallet (or any other wallet that implements our SDK interfaces).

The ShoCard wallet also utilizes our ShoCard Server SDK, just as a service endpoint might, though designed for native mobile applications.

ShoCard Server SDK overview

Flow diagram

Implemented in a service endpoint, the ShoCard Server SDK enables a service to create a QR code with instructions for a ShoCard 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 ShoCard app to establish a communication session. The server can then index a receiving request using the supplied ss_id.

After the ShoCard mobile app scans the QR code, it can create a secure envelope on the device that includes the ss_id, the requested information, and the ShoCard app's own shocardID. This data is then signed by the private key of the mobile device user in the ShoCard wallet, and encrypted with the public key of the service. The ShoCard 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 ShoCard 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 the ShoCard mobile app or a service endpoint.

Creating verifiable credentials

When a service receives user information from a ShoCard app, it can store the user’s shocardID for future reference and communication. The service sends messages using the user's shocardID through the ShoCard Server SDK. The messages are routed using the ID Routing service, and sent to the 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.

The service endpoint can create any credential it finds appropriate for the user. These credentials are referred to as a Card within the ShoCard app. Each card can contain a graphic display in SVG format, along with any set of key/value pairs. For example, a bank may issue a card 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 card data using the ShoCard Server SDK. Using this process, a verifiable claim is created. In the ShoCard 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 ShoCard 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 ShoCard Server SDK on the service endpoint then sends back to the user in a secure envelope:

  • The card (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 the ShoCard app on their mobile device, the user can now share with any other application or service this new card (or any individual key/value pairs), along with the verifiable proof of the information received from the service endpoint.

Class Reference

ApplicationInstance

The ApplicationInstance class represents a particular instance of either a ShoCard mobile application or a ShoCard server application.

The ShoCard mobile or 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 ShoCard application.
jwks The JSON Web Key Set (JWKS) the application will use to verify the ShoCard server Java Web Token (JWT).
pushToken A string used to send push notifications to ShoCard mobile apps or callback URLs for ShoCard server applications.
osType A string identifying the ShoCard mobile app or server application operating system.
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 ShoCard 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.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 ShoCard 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 ShoCard 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 ShoCard 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 ShoCard 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 ShoCard application instance to another trusted ShoCard 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 ShoCard 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 ShoCard application instance receives a message containing some data and a claim from another ShoCard 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 ShoCard 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 ShoCard 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.