Getting Started with the PingOne Recognize Mobile SDK
Learn how to integrate the PingOne Recognize SDK in your Android or iOS mobile application, and enroll and authenticate users through the PingOne Recognize platform.
Before jumping into your code editor, make sure that you are familiar with the various components of the authentication system and common biometric integration flows.
Before you begin
Make sure you have both required API keys and the list of production hosts from your PingOne Recognize contact:
-
YOUR_CLOUDSMITH_TOKENto download the SDK from Cloudsmith repository -
KEYLESS_API_KEYto configure the mobile SDK -
KEYLESS_HOSTSa list of node URLs. URLs inKEYLESS_HOSTSmust not contain trailing slashes (/).
PingOne Recognize SDK requirements.
-
Android
-
iOS
-
Flutter
-
React Native
The PingOne Recognize SDK uses:
-
Android 6.0 (API level 23) and above
The PingOne Recognize SDK uses:
Set up a physical iOS device for running your app and enable the following permissions:
-
Enable camera permissions: add the
Privacy - Camera Usage Descriptionkey in your projectInfo.plist(in Xcode under Project > Info):<key>NSCameraUsageDescription</key> <string>{p1recognize} needs access to your camera to enroll and authenticate you. {p1recognize} cannot be used without your camera. Please allow camera permissions.</string> -
Enable background processing to synchronize PingOne Recognize data. You can find a comprehensive guide in Apple documentation. In short, enable the
Background processingmode underSigning & Capabilities/Background Modes. Then add the following in yourInfo.plistunderPermitted background task scheduler identifiers:<key>NSCameraUsageDescription</key> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>YOUR APP IDENTIFIER</string> </array>
The PingOne Recognize SDK uses:
-
Flutter 3.0 or higher
-
Dart 3.0 or higher
-
All requirements from both Android and iOS platforms
Set up required permissions as described in the Android and iOS sections.
The PingOne Recognize RN SDK requires:
-
React 18.2.0 or higher
-
React Native 0.71.0 or higher
Additionally, ensure you meet the native requirements for each platform:
-
Android: Minimum SDK 23 (Android 6.0), Kotlin
2.2.0or higher. -
iOS: Target version
13.0or higher
Installation
|
PingOne Recognize Anti-Inject variant for enhanced frame injection prevention If you are using the anti-inject SDK variant with runtime application self protection (RASP), you need to: |
-
Set the package repository
partners-rasp-*communicated to you by the Delivery team. -
Add into your app assets the
licensefile provided by the Delivery team.
-
Android - Gradle
-
iOS - SPM
-
iOS - CocoaPods
-
Flutter
-
React Native
-
To allow PingOne Recognize to handle the result of registerForActivityResult, you must call PingOne Recognize from an Activity implementing ActivityResultCaller.
Extend any AndroidX activity that implements this interface, for example ComponentActivity or AppCompatActivity.
-
If you use Proguard, add the following rules to your Proguard configuration file:
# {p1recognize} Proguard -keep class io.keyless.sdk.** {*;} -keepclassmembers class io.keyless.sdk.** {*;} -
In the
repositoriessection of thesettings.gradlefile of your Android app, add the snippet below, replacingYOUR_CLOUDSMITH_TOKENwith the CloudSmith token provided to you by PingOne Recognize.dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() maven { url "https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/maven/" } } } -
In the
dependenciesblock of your projectbuild.gradlefile, typicallyapp/build.gradle, add:dependencies { // ... implementation 'io.keyless:keyless-mobile-sdk:+' } -
In the
androidblock of your projectbuild.gradlefile, make sure you have:android { // ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // Add only if you're using Kotlin kotlinOptions { jvmTarget = "1.8" } }
Cloudsmith distributes artifacts and works as a Swift Package Registry. To use packages from a registry, set up the environment for that registry and log in.
When working with Swift projects, registries can be configured locally to the project. Xcode does not support local registry configuration, so configure globally for the editor to detect packages.
| It is best to run the following commands from a terminal with Xcode closed. |
-
To configure the PingOne Recognize Cloudsmith Package Registry, run:
swift package-registry set --global --scope keyless https://swift.cloudsmith.io/keyless/partners/ swift package-registry login https://swift.cloudsmith.io/keyless/partners/ --token <your-api-token> -
Use the token provided to you in place of
<your-api-token>. -
Because this registry is global, provide the scope
keylessfor the PingOne Recognize Package Registry.These commands add the following to
~/.swiftpm/configuration/registries.json:{ "authentication" : { "swift.cloudsmith.io" : { "loginAPIPath" : "/keyless/<repo>", "type" : "token" } }, "registries" : { "keyless" : { "supportsAvailability" : false, "url" : "https://swift.cloudsmith.io/keyless/<repo>/" } }, "version" : 1 } -
After setup, open Xcode, go to File > Add Package Dependencies, and search for package
keyless.mobile-sdk.
Optional: Test Release Candidates
Release candidates are available in a separate repo. Because Xcode does not handle semantic versioning with build numbers well, use this approach:
-
Download the package manually from Cloudsmith with your token.
-
Extract the archive to a folder named
KeylessSDKPackage. -
Drag and drop the extracted folder inside your Xcode project, under the project root.
If a PingOne Recognize SDK package is already configured, it is seamlessly replaced by the local one. Otherwise, add KeylessSDK library to your target from project settings.
| Because CocoaPods is in maintenance mode, this method will be discontinued. Prefer SPM. |
-
Create a Podfile if you do not already have one:
cd your-project-directory pod init -
Add the
keyless-mobile-sdkpod to yourPodfile:# This is the {p1recognize} repository for partners source 'https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/cocoapods/index.git' target 'MyApp' do use_frameworks! # Add the {p1recognize} pod pod 'keyless-mobile-sdk' end -
Add the following at the bottom of your
Podfile:post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['ENABLE_BITCODE'] = 'NO' end end end -
Install pods:
pod install Analyzing dependencies Cloning spec repo `cloudsmith-YOUR_CLOUDSMITH_TOKEN-keyless-partners-cocoapods-index` from `https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/cocoapods/index.git`
-
Add the PingOne Recognize SDK repository:
echo '${CLOUDSMITH_TOKEN}' | dart pub token add https://dart.cloudsmith.io/keyless/flutter/ -
Add the PingOne Recognize SDK dependency to
pubspec.yaml:dart pub add keyless_flutter_sdk:PACKAGE_VERSION --hosted-url https://dart.cloudsmith.io/keyless/flutter/ -
Follow installation steps in both Android and iOS sections to set up native SDKs.
-
Run
flutter pub getto download dependencies:flutter pub get
You also need to bridge native SDKs used by the Flutter SDK in the android and ios sections of your Flutter project.
Android
-
Add the following line in root
build.gradle:allprojects { repositories { google() mavenCentral() maven { setUrl("https://dl.cloudsmith.io/YOUR_CUSTOM_TOKEN/keyless/partners/maven/") } } } -
Replace
YOUR_CUSTOM_TOKENwith the Cloudsmith token provided by the PingOne Recognize integration support team. -
Then open your
Android.manifestand add the following inside the<application>tag:<provider android:name="androidx.startup.InitializationProvider" android:authorities="${applicationId}.androidx-startup" android:exported="false" tools:node="merge"> <meta-data android:name="io.keyless.fluttersdk.KeylessInitializer" android:value="androidx.startup" /> </provider>
iOS
-
Target at least iOS 13 in your project.
-
Open your
PodFileand add:source 'https://dl.cloudsmith.io/YOUR_CUSTOM_TOKEN/keyless/partners/cocoapods/index.git' -
Add the following permissions in
Info.plist:<key>NSCameraUsageDescription</key> <string>{p1recognize} needs access to your camera to enroll and authenticate you. {p1recognize} cannot be used without your camera. Please allow camera permissions.</string> <key>NSMicrophoneUsageDescription</key> <string>{p1recognize} needs access to your camera to enroll and authenticate you. {p1recognize} cannot be used without your camera. Please allow camera permissions.</string>
-
Configure access to the private registry by creating a
.npmrcfile in your project root:@react-native-keyless:registry=https://npm.cloudsmith.io/keyless/partners-rc/ //npm.cloudsmith.io/keyless/partners/:_authToken=YOUR_CLOUDSMITH_TOKENReplace
YOUR_CLOUDSMITH_TOKENwith the token provided to you. -
Add the PingOne Recognize SDK dependency:
# Using npm npm install @react-native-keyless/sdk # Using yarn yarn add @react-native-keyless/sdk
Android Setup
-
Add the PingOne Recognize Maven repository to root
build.gradle:allprojects { repositories { google() mavenCentral() maven { url "https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/maven/" } } } -
Verify Kotlin version is
2.2.0or higher in your Android plugin configuration.
iOS Setup
-
Ensure
platform :ios, '13.0'or higher inios/Podfile. -
Add private spec repo at top of Podfile:
source 'https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/cocoapods/index.git' source 'https://cdn.cocoapods.org/' -
Add camera permissions in
ios/Info.plist:<key>NSCameraUsageDescription</key> <string>This app uses the camera to scan face.</string> -
Install native dependencies:
cd ios && pod install
Essential Configuration
There is some essential configuration required before you can use the PingOne Recognize SDK.
-
Android
-
iOS
-
Android v4.6
-
iOS v4.6
-
Flutter
-
React Native
-
Initialize PingOne Recognize SDK in your
Applicationclass:// MainApplication override fun onCreate() { super.onCreate() // Initialize {p1recognize} Keyless.initialize(this) }If your app crashes with java.lang.IllegalStateException: Attempting to launch an unregistered ActivityResultLauncher(or React Native bridges do not receive enrollment completion), this may be due to a mismatch betweenapplicationIdand Activitynamespace.In this case, pass your Activities namespace as a second parameter:
// MainApplication override fun onCreate() { super.onCreate() // Initialize {p1recognize} Keyless.initialize(this, "my.app.test") } -
Add your application class to
AndroidManifest.xml:<application android:allowBackup="true" android:name=".MainApplication" android:label="@string/app_name" android:supportsRtl="true"> </application> -
Configure PingOne Recognize SDK from
MainActivity,ViewModel, or your PingOne Recognize integration class.configureis asynchronous, so wait for completion before calling other APIs:val setupConfig = SetupConfig( apiKey = "KEYLESS_API_KEY", hosts = listOf("KEYLESS_HOSTS") ) Keyless.configure(setupConfig) { result -> when (result) { is Keyless.KeylessResult.Success -> { Log.d("KeylessSDK", "configure success") // {p1recognize} is ready } is Keyless.KeylessResult.Failure -> { Log.d("KeylessSDK", "configure error") } } }
-
Create
SetupConfigand pass it toKeyless.configure(typically inapplication(:didFinishLaunchingWithOptions:)):let setupConfig = SetupConfig( apiKey: "KEYLESS_API_KEY", hosts: ["KEYLESS_HOSTS"] ) if let error = Keyless.configure(configuration: setupConfig) { print("{p1recognize}.Configure failed with error: \(error)") }
-
Initialize PingOne Recognize SDK:
// MainApplication override fun onCreate() { super.onCreate() Keyless.initialize(this) } -
Add your application class to the manifest.
-
Configure using
SetupConfiguration:val setupConfiguration = SetupConfiguration.builder .withApiKey("KEYLESS_API_KEY") .withHosts(listOf("KEYLESS_HOSTS")) .build() Keyless.configure(setupConfiguration) { result -> when (result) { is Keyless.KeylessResult.Success -> { Log.d("KeylessSDK", "configure success") } is Keyless.KeylessResult.Failure -> { Log.d("KeylessSDK", "configure error") } } }
-
Create
Keyless.SetupConfigurationand pass it toKeyless.configure:let setupConfiguration = Keyless.SetupConfiguration.builder .withApiKey("KEYLESS_API_KEY") .withHosts(["KEYLESS_HOSTS"]) .build() if let error = Keyless.configure(configuration: setupConfiguration) { print("Keyless.Configure failed with error: \(error)") }
-
Import required packages:
import 'package:keyless_flutter_sdk/keyless.dart'; import 'package:keyless_flutter_sdk/models/configurations/setup_configuration.dart'; -
Configure SDK during app initialization:
final setupConfiguration = SetupConfiguration( apiKey: "KEYLESS_API_KEY", hosts: ["KEYLESS_HOSTS"], ); try { await Keyless.instance.configure(setupConfiguration); print("Keyless SDK configured successfully"); } catch (error) { print("Failed to configure Keyless SDK: $error"); }
-
Initialize native SDK for Android in
MainApplication.kt:import io.reactnative.keyless.sdk.KeylessSDKModule override fun onCreate() { super.onCreate() KeylessSDKModule.initialize(application = this) } -
Configure SDK once app starts:
import Keyless, { SetupConfig } from '@react-native-keyless/sdk'; const config = new SetupConfig({ apiKey: 'KEYLESS_API_KEY', hosts: ['KEYLESS_HOSTS'], }); const result = await Keyless.configure(config); result.fold({ onSuccess: data => { console.log('Keyless SDK configured successfully:', data); }, onFailure: error => { console.error('Failed to configure Keyless SDK:', error); }, });
Additional Configuration
In addition to essential configuration parameters, you can also specify optional parameters detailed throughout Mobile SDK docs (for example, Logging, Liveness Settings).
Shared Circuits
This is the target desired number of circuits kept on the server. Use numberOfSharedCircuits to set this value.
Networking Module
If you need to perform network requests on behalf of the Keyless SDK, implement the KLNetworkingModule interface and set networkingModule with your implementation.
-
Android
-
iOS
public interface KLNetworkingModule {
public fun sendHTTPRequest(
baseUrl: String,
path: String,
method: String,
headers: Map<String, String>,
body: String
): KLHTTPResponse
public data class KLHTTPResponse(
val errorCode: Int,
val httpCode: Int,
val responseBody: String
)
}
public protocol KLNetworkingModule {
func sendHTTPRequest(
host: String,
url: String,
method: String,
headers: [String: String],
body: String
) -> KLHTTPResponse
}
public struct KLHTTPResponse {
var errorCode: Int
var httpCode: Int
var responseBody: String
public init(errorCode: Int, httpCode: Int, responseBody: String) {
self.errorCode = errorCode
self.httpCode = httpCode
self.responseBody = responseBody
}
}