Documentation Hub
Mobile SDK
Mobile SDK
  • Keyless SDK Documentation
  • Introduction
    • 🧩Components
    • ⚙️Integration Flows
    • 🤳Sample Apps
  • 📱Mobile SDK Guide
    • 1️⃣ Getting started
    • 2️⃣ Enrollment
    • 3️⃣ Authentication
    • 4️⃣ De-Enrollment
    • 5️⃣ Backup
    • 6️⃣ User and device management
  • 📱Mobile SDK Reference
    • UI Customization
    • Error handling
    • Liveness Settings
    • JWT signing
    • PIN authentication
    • Introduce Keyless to Users
    • Photo Enrollment
  • 📱Mobile SDK Use Cases
    • Account recovery
    • Dynamic Linking
  • 📒Mobile SDK Changelog
    • Changelog
  • 💽Server API
    • Getting Started
    • Users
    • Devices
    • Operations
Powered by GitBook
On this page
  • Prerequisites
  • Installation
  • Essential configuration
  • Additional configuration
  • Logging
  • Liveness Lockout policy
  • Tampered device check
  • Enrollment circuits
  • Shared circuits
  • Networking module

Was this helpful?

  1. Mobile SDK Guide

1️⃣ Getting started

Last updated 56 minutes ago

Was this helpful?

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 of the authentication system, and common biometic .

Prerequisites

Make sure you have both required API keys, and the list of productions hosts from your Keyless contact:

  • YOUR_CLOUDSMITH_TOKEN to download the SDK from cloudsmith repository

  • KEYLESS_API_KEY to configure the mobile SDK

  • KEYLESS_HOSTS a list of node URLs. Urls in KEYLESS_HOSTS must NOT contain any trailing slashes /.

Review the Keyless SDK requirements:

The Keyless SDK uses

  • (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 Description key in your project’s Info.plist by 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 . In a nutshell here is what you need to do: enable the Background processing mode under Signing & Capabilities/Background Modes. Then, add the following in your project’s Info.plit (in XCode, under the key Permitted background task scheduler identifiers):

    <key>NSCameraUsageDescription</key>
    <key>BGTaskSchedulerPermittedIdentifiers</key>
    <array>
        <string>YOUR APP IDENTIFIER</string>
    </array>

The Keyless SDK uses

  • All requirements from both Android and iOS platforms

Set up the required permissions as described in the Android and iOS tabs.

Installation

  1. 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.** {*;}
  2. In the the repositories section of the settings.gradle file of your Android application, add the following snippet, replacing YOUR_CLOUDSMITH_TOKEN with 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/"
            }
        }
    }
  3. In the dependencies block of your project build.gradle file, typically app/build.gradle, add:

    dependencies {
    // ...
    
    implementation 'io.keyless:keyless-android-sdk:+'
    }
  4. In the android block of your project build.gradle file, typically app/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"
        }
    }
  1. Create a Podfile if you don’t already have one

    $ cd your-project-directory
    $ pod init
  2. Set up Cocoapods to use your Cloudsmith credentials, by running the following commands, replacing YOUR_CLOUDSMITH_TOKEN with the Cloudsmith token provided you by Keyless.

    git config --global credential.helper store
    echo "https://token:YOUR_CLOUDSMITH_TOKEN@dl.cloudsmith.io" >> ~/.git-credentials
  3. Add the KeylessSDK pod to your Podfile

    # This is the Keyless repository for partners
    source 'https://dl.cloudsmith.io/basic/keyless/partners/cocoapods/index.git'
    
    target 'MyApp' do
        use_frameworks!
    
        # Add the Keyless pod
        pod 'KeylessSDK'
    end
  4. Add the following at the bottom of you 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
  5. Install the pods. If pod prompts you for authentication insert the string token as your username and then fill in YOUR_CLOUDSMITH_TOKEN as the password.

    $ pod install
    
    Analyzing dependencies
    Cloning spec repo `cloudsmith-basic-keyless-partners-cocoapods-index` from `https://dl.cloudsmith.io/basic/keyless/partners/cocoapods/index.git`
    Username for 'https://dl.cloudsmith.io': token
    Password for 'https://dl.cloudsmith.io': YOUR_CLOUDSMITH_TOKEN
  1. Add the Keyless SDK repository

    echo '${CLOUDSMITH_TOKEN}' | dart pub token add https://dart.cloudsmith.io/keyless/flutter/
  2. 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/
  3. Follow the installation steps in both the Android and iOS tabs to set up the native SDKs in their respective platforms.

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

Essential configuration

There is some essential configuration to do before you can use the Keyless SDK.

  1. Initialize the Keyless SDK in your Application class:

    // MainApplication
    override fun onCreate() {
        super.onCreate()
        // Initialize Keyless
        Keyless.initialize(this)
    }
  2. Add your application to the Manifest

    <application
        ...
        android:name=".MainApplication"
        ...
    </application>
  3. Configure the Keyless SDK from your MainActivity, ViewModel or any class you use to communicate with Keyless. Note that configure is asynchronous so wait for the completion callback before calling the next Keyless APIs

    The configure method requires a SetupConfig as parameter, as well as the KEYLESS_API_KEY and KEYLESS_HOSTS you 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
             }
         }
     }
  1. Create an instance object of Keyless.SetupConfiguration and pass it to the Keyless.configure method, typically this is done in your app’s application(: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)")
    }
  1. Initialize the Keyless SDK in your Application class:

    // MainApplication
    override fun onCreate() {
        super.onCreate()
        // Initialize Keyless
        Keyless.initialize(this)
    }
  2. Add your application to the Manifest

    <application
        ...
        android:name=".MainApplication"
        ...
    </application>
  3. Configure the Keyless SDK from your MainActivity, ViewModel or any class you use to communicate with Keyless. Note that configure is asynchronous so wait for the completion callback before calling the next Keyless APIs

    The configure method requires a SetupConfiguration as parameter, as well as the KEYLESS_API_KEY and KEYLESS_HOSTS you 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
             }
         }
     }
  1. Create an instance object of Keyless.SetupConfiguration and pass it to the Keyless.configure method, typically this is done in your app’s application(: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)")
    }
  1. Import the required packages:

    import 'package:keyless_flutter_sdk/keyless.dart';
    import 'package:keyless_flutter_sdk/models/configurations/setup_configuration.dart';
  2. 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
    }
  3. Make sure to set up the required permissions in both Android and iOS platforms as described in their respective tabs.

Additional configuration

As well as the essential configuration parameters, you can also specify the optional configuration parameters detailed in the following sections. You can inspect the Config class to see optional parameters (or the builder to see exposed methods)

Logging

Logging is disabled by default.

Please note logs do not include any Personally Identifiable Information (PII).

Keyless allows you to:

  1. enable logging to the Keyless infrastructure - keylessLogsConfiguration

  2. collect logging for you own analytics without sending them to the Keyless infrastructure - customLogsConfiguration

val setup = SetupConfig(
    apiKey = "...",
    hosts = listOf(""),
    keylessLogsConfiguration = LogsConfiguration(
        enabled = true,
        logLevel = LogLevels.INFO
    ),
    customLogsConfiguration = LogsConfiguration(
        enabled = true
    )
)
let configuration = SetupConfig(
    apiKey: "some api key",
    hosts: ["some.host"],
    keylessLogsConfiguration: KeylessLogsConfiguration(enabled: true),
    customLogsConfiguration: CustomLogsConfiguration(enabled: true, logLevel: .INFO, callback: { event in
        print(event)
    })
)

Keyless.configure(setupConfiguration: configuration) { error in
    // handle error
}
val setupConfiguration = SetupConfiguration.builder
    .withApiKey("")
    .withHosts(listOf("..."))
    .withLogging(
        keylessLogsConfiguration = LogsConfiguration(enabled = true),
        customLogsConfiguration = LogsConfiguration(enabled = true, logLevel = LogLevels.INFO)
    )
    .build()
let configuration = Keyless.SetupConfiguration
  .builder
  .withApiKey("some api key")
  .withHosts(["some.host"])
  .withLogging(
    keylessLogsConfiguration: KeylessLogsConfiguration(enabled: true, logLevel: .INFO),
    customLogsConfiguration: CustomLogsConfiguration(enabled: true, callback: { event in
      print(event)
    })
  )
  .build()
 
Keyless.configure(setupConfiguration: configuration) { error in
  // handle error
}
final configuration = SetupConfiguration(
              apiKey: apiKey,
              hosts: [host],
              loggingEnabled: true,
              loggingLevel: LogLevel.info));

try {
  final result = await Keyless.instance.configure(configuration);
  print("Configure finished succcessfully.");
} catch (error) {
  print("Configure finished with error: $error");
}

Collect logs after setting up custom logging with customLogsConfiguration option:

  1. Start collecting the Keyless.customLogs flow (This must happen before a Keyless.configure() to get all Logs Events):

Keyless.customLogs.collect { logEvent ->
    // handle the logEvent
}
  1. Configure the SDK with the following SetupConfig:

val setup = SetupConfig(
    apiKey = "...",
    hosts = listOf(""),
    customLogsConfiguration = LogsConfiguration(
        enabled = true,
        logLevel = LogLevels.INFO // This is optional and defaults to INFO
    )
)
var myEventCollection = [LogEvent]()
    let configuration = Keyless.SetupConfiguration
      .builder
      .withApiKey("some api key")
      .withHosts(["some.host"])
      .withLogging(
        keylessLogsConfiguration: KeylessLogsConfiguration(enabled: true, logLevel: .INFO),
        customLogsConfiguration: CustomLogsConfiguration(enabled: true, callback: { event in
          myEventCollection.append(event)
        })
      )
     .build()
    Keyless.configure(setupConfiguration: configuration) { error in
      // handle error
    }
}

Logging Levels

You may set different logging levels to provide more or less information in logs.

The available levels are the following:

  • INFO (default)

  • DEBUG

  • TRACE

The TRACE level provides the following additional data:

  • userId

  • devicePublicSigningKey

  • coreLogHistory (used for detailed debugging)

Liveness Lockout policy

Since version 5.0.0 of the SDK the lockout policy is no longer processed on the client for security reasons. The lockout policy is applied only during authentication and can be confiured server side.

Keyless has both client side (applicable to a specific device) and server side (applicable to all users and devices) lockout policies to help prevent brute force attacks.

Client side lockout is configurable in the SDK, and determines how many failed login attempts (lockoutAttemptsThreshold) are allowed over a set time period (lockoutAttemptsResetAfter) before the user is locked out for the set duration (lockoutDuration) on that device.

Account lockoutDuration must be greater than or equal to the lockoutAttemptsResetAfter so that it is not reset by lockoutAttemptsResetAfter.

lockoutDuration: Long,                //seconds - default 300 seconds
lockoutAttemptsResetAfter: Long,      //seconds - default 180 seconds
lockoutAttemptsThreshold: Int         //number  - default 5 attempts

Server side lockout works similarly, except applies to all authentication devices for a specific user, and is configured to lock a user out for 10 minutes after 5 failed attempts. A successful login resets the count of failed authentication attempts to zero.

Tampered device check

Keyless provides a tampered device check that can be enabled within the Mobile SDK configurations. This service looks for signs of rooting, or jailbreaking, whereby a smartphone or tablet may have been tampered with in order to gain greater administrative control over it, a tactic used by malicious actors. You can enable the check by:

  • Calling the withTamperedDeviceCheck() on the SetupConfigurationBuilder if using the legacy SetupConfiguration.

  • Setting withDevicePrivileges to true if using the new SetupConfig.

If you enable this check, and the SDK detects that the phone was tampered with, the Keyless SDK will return an error and prevent the user from authenticating. As ever, we advise clients that no security measure is perfect and we can't guarantee 100% detection success rate. Therefore we recommend taking advice or carry out further research on how else to harden the security of your own app if this is a particular concern.

Enrollment circuits

To speed up enrollment you can use the numberOfEnrollmentCircuits to upload less than the default of five circuits upon enrollment. The remainder (50 - numberOfEnrollmentCircuits) are uploaded asynchronously.

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

or higher

or higher

To allow Keyless to handle the result of , you must call Keyless from an Activity implementing .

Extend any androidX activity that implements the interface for you, for example let your Activity extend the generic or the more widespread .

📱
components
authentication flows
Android 6.0
Kotlin 1.9.25
Gradle 8.7
Android Gradle Plugin 8.3.0
AndroidX
iOS 13
Swift 5.1
Cocoapods 1.15.2
apple documentation
Flutter 3.0
Dart 3.0
registerForActivityResult
ActivityResultCaller
ComponentActivity
AppCompatActivity