1️⃣ Getting started
In this short guide, you will learn how to integrate the Keyless SDK in your Android or iOS mobile application, and enroll and authenticate users through the Keyless platform.
Before jumping into your code editor, make sure that you're familiar with the various components of the authentication system, and common biometic authentication flows.
Prerequisites
Make sure you have both required API keys, and the list of productions hosts from your Keyless contact:
YOUR_CLOUDSMITH_TOKENto download the SDK from cloudsmith repositoryKEYLESS_API_KEYto configure the mobile SDKKEYLESS_HOSTSa list of node URLs. Urls inKEYLESS_HOSTSmust NOT contain any trailing slashes/.
Review the Keyless SDK requirements:
The Keyless SDK uses
Android 6.0 (API level 23) and above
The Keyless 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 project’sInfo.plistby adding the following (in XCode under Project > Info):<key>NSCameraUsageDescription</key> <string>Keyless needs access to your camera to enroll and authenticate you. Keyless cannot be used without your camera. Please allow camera permissions.</string>Enable background processing: necessary to synchronize Keyless data. You can find a comprehensive guide in the apple documentation. In a nutshell here is what you need to do: enable the
Background processingmode underSigning & Capabilities/Background Modes. Then, add the following in your project’sInfo.plit(in XCode, under the keyPermitted background task scheduler identifiers):<key>NSCameraUsageDescription</key> <key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>YOUR APP IDENTIFIER</string> </array>
The Keyless SDK uses
Flutter 3.0 or higher
Dart 3.0 or higher
All requirements from both Android and iOS platforms
Set up the required permissions as described in the Android and iOS tabs.
The Keyless 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
To allow Keyless to handle the result of registerForActivityResult, you must call Keyless from an Activity implementing ActivityResultCaller.
Extend any androidX activity that implements the interface for you, for example let your Activity extend the generic ComponentActivity or the more widespread AppCompatActivity.
If you use Proguard, add the following rules to your Proguard configuration file:
# Keyless Proguard -keep class io.keyless.sdk.** {*;} -keepclassmembers class io.keyless.sdk.** {*;}In the the
repositoriessection of thesettings.gradlefile of your Android application, add the following snippet, replacingYOUR_CLOUDSMITH_TOKENwith the CloudSmith token provided to you by Keyless.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-android-sdk:+' }In the
androidblock of your projectbuild.gradlefile, typicallyapp/build.gradle, make sure you have the following options:android { // ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } // add the following only if you're using Kotlin kotlinOptions { jvmTarget = "1.8" } }
We use Cloudsmith to distribute our artifacts. Cloudsmith works as a Swift Package Registry. In order to use packages from a registry, one has to set up the environment for that specific registry and possibly log in to it.
When working with Swift Projects, registries can be configured locally to the project. Unfortunately, though, Xcode does not support local configuration of registries, thus they have to be configured globally, for the editor to be able to see them.
It's best to run all the following commands from terminal with Xcode closed
To configure Keyless Cloudsmith Package Registry, you can run the following commands in your terminal
swift package-registry set --global --scope <package-scope> https://swift.cloudsmith.io/keyless/<repo>/
swift package-registry login https://swift.cloudsmith.io/keyless/<repo>/ --token <your-api-token>You shall replace <repo> with the name of the repo that was assigned to you by our delivery team. You will also be provided with a token to put in place of <your-api-token> to access that repo.
Since you are adding this registry as local, it is important to provide a scope <package-scope> (also provided by us) for Keyless Package registry. It may be that you have multiple package registries on Cloudsmith configured, thus we provide a scope to our packages so that it's easy to discriminate between several registries. Yet, you may need to login into every registry, when you are switching between them.
These commands add the following to your ~/.swiftpm/configurations/registries.json
{
"authentication" : {
"swift.cloudsmith.io" : {
"loginAPIPath" : "/keyless/<repo>",
"type" : "token"
}
},
"registries" : {
"[default]" : {
"supportsAvailability" : false,
"url" : "https://swift.cloudsmith.io/keyless/<repo>/"
}
"version" : 1
}Once you have this setup, you can open Xcode, go to File → Add Package Dependencies and a wizard will open to prompt the insertion of the package.
In the top-right bar, you can search for the package within your assigned scope, e.g. keyless.KeylessSDK, and if everything is setup correctly, the package should appear in the center column. You can add the package to the project and proper targets and you are ready to go.
Optional - Test release candidates
We provide our partners the chance to test Release candidates before they receive the final approval of our whole QA process. Since release candidates are stored in a separate repo and Xcode does not cope well with Semantic versioning with build numbers (which our release candidates use), we suggest to:
Manually download the Package from Cloudsmith, using your assigned token
Extract the archive. It should unpack in a folder called KeylessSDKPackage
Drag-and-drop the extracted folder inside your Xcode project, exactly under the root of the project file.
Once dropped inside your project, if you already had a Keyless SDK Package configured with the first method, this is going to be substituted by the local one seamlessly. If not, then just add the KeylessSDK library to your target from the project settings.
Since Cocoapods is in maintenance mode, this method is going to be discontinued. Please integrate Keyless via SPM
Create a Podfile if you don’t already have one
$ cd your-project-directory $ pod initAdd the KeylessSDK pod to your
Podfile# This is the Keyless repository for partners source 'https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners/cocoapods/index.git' target 'MyApp' do use_frameworks! # Add the Keyless pod pod 'KeylessSDK' endAdd the following at the bottom of your
Podfilepost_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| config.build_settings['ENABLE_BITCODE'] = 'NO' end end endInstall the 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 Keyless SDK repository
echo '${CLOUDSMITH_TOKEN}' | dart pub token add https://dart.cloudsmith.io/keyless/flutter/Add the Keyless SDK dependency to your
pubspec.yaml:dart pub add keyless_flutter_sdk:PACKAGE_VERSION --hosted-url https://dart.cloudsmith.io/keyless/flutter/Follow the installation steps in both the Android and iOS tabs to set up the native SDKs in their respective platforms.
Run flutter pub get to download the dependencies:
flutter pub get
You also need to bridge the native SDKs that are used by the flutter SDK. You can do that in the android and ios sections of your flutter project.
Android
Add the following line in the root build.gradle:
allprojects {
repositories {
google()
mavenCentral()
maven {
setUrl("https://dl.cloudsmith.io/YOUR_CUSTOM_TOKEN/keyless/partners/maven/")
}
}
} Where YOUR_CUSTOM_TOKEN should be replaced with the cloudsmith token provided by the Keyless integration support team.
In the example repo you can see that we are using a file to simulate env properties containing the cloudsmith token. You will read the following snippet:
allprojects {
repositories {
google()
mavenCentral()
maven {
setUrl("https://dl.cloudsmith.io/$cloudsmithToken/keyless/partners/maven/")
}
}
}You can read the cloudsmith token from a file cloudsmith.properties that you need to create at the root project directory.
File content shall be the following: cloudsmithToken=YOUR_CUSTOM_TOKEN
Then, open your Android.manifest file and add the following lines 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
Make sure to target at least iOS 13 in your project.
Open your PodFile and add the following line:
source 'https://dl.cloudsmith.io/YOUR_CUSTOM_TOKEN/keyless/partners/cocoapods/index.git'Where YOUR_CUSTOM_TOKEN should be replaced with the cloudsmith token provided by the Keyless integration support team.
In the example repo you can see that we are using a file to simulate env properties containing the cloudsmith token. You will read the following snippet:
source 'https://dl.cloudsmith.io/'+ ENV['cloudsmithToken'] +'/keyless/partners/cocoapods/index.git'File content shall be the following: cloudsmithToken=YOUR_CUSTOM_TOKEN
Then, navigate to your Info.plist file, typically located in a folder called with your project name (e.g. Runner).
Add the following permissions declaration to this file (if not present already):
<key>NSCameraUsageDescription</key>
<string>Keyless needs access to your camera to enroll and authenticate you. Keyless cannot be used without your camera. Please allow camera permissions.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Keyless needs access to your camera to enroll and authenticate you. Keyless cannot be used without your camera. Please allow camera permissions.</string> 1. Configure Access to the Private Registry
Create a .npmrc file in the root of your project. This file will tell your package manager where to find the Keyless SDK and how to authenticate. Add the following lines:
@react-native-keyless:registry=https://npm.cloudsmith.io/keyless/partners-rc/
//npm.cloudsmith.io/keyless/partners-rc/:_authToken=YOUR_CLOUDSMITH_TOKENNote: Replace YOUR_CLOUDSMITH_TOKEN with the actual token provided to you.
2. Add the Keyless SDK Dependency
Run one of the following commands in your project's terminal to install the latest version of the SDK.
# Using npm
npm install @react-native-keyless/sdk
# Using yarn
yarn add @react-native-keyless/sdkAfter installing the package, follow the setup steps for each native platform below.
Android Setup
Add the Maven Repository: Add the Keyless Maven repository to your root
build.gradlefile:allprojects { repositories { google() mavenCentral() maven { url "https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners-rc/maven/" } } }Note: Replace
YOUR_CLOUDSMITH_TOKENwith your token.Verify Kotlin Version: The Keyless SDK requires a minimum Kotlin version of
2.2.0. Depending on your React Native project's configuration, this is specified in one of two files.If your project uses
settings.gradlefor plugins (modern setup): Open yourandroid/settings.gradlefile and ensure the version fororg.jetbrains.kotlin.androidis2.2.0or higher.// android/settings.gradle pluginManagement { plugins { // ... other plugins id "org.jetbrains.kotlin.android" version "2.2.0" apply false // ✅ Must be 2.2.0 or higher } }If your project uses
build.gradlefor plugins (older setup): Open your rootandroid/build.gradlefile. ThekotlinVersionvariable is defined in theextblock and then used in thedependenciesblock. Proverite da je verzija2.2.0or higher.// android/build.gradle buildscript { ext { // ... other variables kotlinVersion = "2.2.0" // ✅ 1. Define version here (must be 2.2.0 or higher) } repositories { google() mavenCentral() } dependencies { // ... other classpaths classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") // ✅ 2. The variable is used on this line } }
iOS Setup
Target iOS 13 or Higher: In your
ios/Podfile, ensure the platform version is set to13.0or higher:platform :ios, '13.0'.Add the Private Spec Repo: Open your
ios/Podfileand add the Keyless Cocoapods source at the top of the file:# Add the Keyless source at the top source 'https://dl.cloudsmith.io/YOUR_CLOUDSMITH_TOKEN/keyless/partners-rc/cocoapods/index.git' source 'https://cdn.cocoapods.org/'Note: Replace
YOUR_CLOUDSMITH_TOKENwith your token.Enable Camera Permissions: Open your
ios/Info.plistfile and add the camera usage description.<key>NSCameraUsageDescription</key> <string>This app uses the camera to scan face.</string>Install Native Dependencies: Navigate to the
iosdirectory and runpod install.cd ios && pod install
Essential configuration
There is some essential configuration to do before you can use the Keyless SDK.
Initialize the Keyless SDK in your
Applicationclass:// MainApplication override fun onCreate() { super.onCreate() // Initialize Keyless Keyless.initialize(this) }
If your application crashes due to java.lang.IllegalStateException: Attempting to launch an unregistered ActivityResultLauncher or while using React/Native bridges you can't receive a completion result at the end of an enrollment, it may be due to a difference between your applicationId and the namespace of your activities.
In such case, the Keyless.initialize(...) accepts a second parameter which is the namespace you are currently using for your Activities. Providing this parameter should fix the issue.
Example:
namespace: my.app.test
applicationId: my.app.test.extra
// MainApplication
override fun onCreate() {
super.onCreate()
// Initialize Keyless
Keyless.initialize(this, "my.app.test")
}Since we only check that the namespace of the Activity contains the namespace provided as input, .app or my.app are also valid inputs.
Add your application to the Manifest
<application ... android:name=".MainApplication" ... </application>Configure the Keyless SDK from your
MainActivity,ViewModelor any class you use to communicate withKeyless. Note thatconfigureis asynchronous so wait for the completion callback before calling the next Keyless APIsThe
configuremethod requires aSetupConfigas parameter, as well as theKEYLESS_API_KEYandKEYLESS_HOSTSyou received from Keyless. You should listen to the result as follows: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") // Keyless is ready, you can now call // enroll/authenticate/deEnroll ... } is Keyless.KeylessResult.Failure ->{ Log.d("KeylessSDK", "configure error") // Inspect result.error for more info } } }
Create an instance object of
Keyless.SetupConfigurationand pass it to theKeyless.configuremethod, typically this is done in your app’sapplication(:didFinishLaunchingWithOptions: method:).let setupConfig = SetupConfig( apiKey: "KEYLESS_API_KEY", hosts: ["KEYLESS_HOSTS"] ) if let error = Keyless.configure(configuration: setupConfig) { print("Keyless.Configure failed with error: \(error)") }
Initialize the Keyless SDK in your
Applicationclass:// MainApplication override fun onCreate() { super.onCreate() // Initialize Keyless Keyless.initialize(this) }Add your application to the Manifest
<application ... android:name=".MainApplication" ... </application>Configure the Keyless SDK from your
MainActivity,ViewModelor any class you use to communicate withKeyless. Note thatconfigureis asynchronous so wait for the completion callback before calling the next Keyless APIsThe
configuremethod requires aSetupConfigurationas parameter, as well as theKEYLESS_API_KEYandKEYLESS_HOSTSyou received from Keyless. You should listen to the result as follows: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") // Keyless is ready, you can now call // enroll/authenticate/deEnroll ... } is Keyless.KeylessResult.Failure ->{ Log.d("KeylessSDK", "configure error") // Inspect result.error for more info } } }
Create an instance object of
Keyless.SetupConfigurationand pass it to theKeyless.configuremethod, typically this is done in your app’sapplication(:didFinishLaunchingWithOptions: method:).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 the required packages:
import 'package:keyless_flutter_sdk/keyless.dart'; import 'package:keyless_flutter_sdk/models/configurations/setup_configuration.dart';Configure the Keyless SDK in your app's initialization code:
final setupConfiguration = SetupConfiguration( apiKey: "KEYLESS_API_KEY", hosts: ["KEYLESS_HOSTS"], ); try { await Keyless.instance.configure(setupConfiguration); print("Keyless SDK configured successfully"); // Keyless is ready, you can now call // enroll/authenticate/deEnroll ... } catch (error) { print("Failed to configure Keyless SDK: $error"); // Handle error }Make sure to set up the required permissions in both Android and iOS platforms as described in their respective tabs.
Initialize the Native SDK for Android: Open your
MainApplication.kt(or.java) file and add the call toKeylessSDKModule.initialize()inside theonCreatemethod.// In MainApplication.kt import io.reactnative.keyless.sdk.KeylessSDKModule // <-- Add this import override fun onCreate() { super.onCreate() // Initialize Keyless KeylessSDKModule.initialize(application = this) // ... rest of your onCreate method }Configure the Keyless SDK: This is best done once when your application starts, after the native SDK has been initialized.
Call the
configuremethod with your credentials. The method returns a result object that you can handle using thefoldmethod.import Keyless, { SetupConfig } from '@react-native-keyless/sdk'; const config = new SetupConfig({ // Make sure to store your keys securely apiKey: 'KEYLESS_API_KEY', hosts: ['KEYLESS_HOSTS'], }); const result = await Keyless.configure(config); result.fold({ onSuccess: data => { console.log('Keyless SDK configured successfully:', data); // Keyless is ready, you can now call other methods }, onFailure: error => { console.error('Failed to configure Keyless SDK:', error); }, });
Additional configuration
As well as the essential configuration parameters, you can also specify the optional configuration parameters detailed in the following sections. Details of these configuration options can be summarised in the relevant sections throughout the Mobile SDK (i.e. Logging, Liveness Settings etc.) and you can inspect the Config class to see optional parameters (or the builder to see exposed methods). Some of the more the more generic configurations are described below for convenience.
Shared circuits
This is the target desired number of circuits kept on the server. To set this number use the numberOfSharedCircuits.
Networking module
If you need to perform network requests on behalf of the Keyless SDK, you can implement the KLNetworkingModule interface. Set the networkingModule parameter with an instance of the KLNetworkingModule:
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
}
}Last updated
Was this helpful?