Mobile SDK: Android

This page will show you how to install and use the FuturaeKit SDK for Android.

Prerequisites

The Android Futurae SDK v2 requires minimum Android version 6.0, API level (minSdkVersion) 23. Before reading the rest of this guide, please make sure that you first read the Mobile SDK: Introduction page.

Release Information

You can find information about the Android FuturaeKit SDK release history and changes here.

Migrate from SDK v1.x.x

Assuming that your app already integrates SDK 1.x.x, the steps to migrate to the latest version v2.x.x, which features the User Presence Verification mechanism, are described below:

  1. Update the SDK version to 2.x.x in your build.gradle file, or for manual installations, get the latest SDK 2.x.x from the SDK repository.
  2. Refactor your app code according to the examples provided across this guide, paying special attention to:
  • Update your SDK launch method and add the new SDK configuration settings, according to the example in the Initialize the SDK section.
  • The FuturaeSDK instance getter is now FuturaeSDK.getClient().
  • The Account class was replaced by the FTAccount class, therefore your app needs to be updated accordingly. Please refer to the Account status section.
  1. Then launch the SDK using the LockConfigurationType as NONE. After launching the SDK with LockConfigurationType.NONE, all the previous data from SDK v1.x.x including accounts and keys will remain valid.
  2. If you wish to use a different LockConfigurationType that supports user presence verification, after launching the SDK using LockConfigurationType.NONE you can perform an SDK configuration switch. Alternatively the SDK must be reset, and initialized again with the new configuration. After resetting the SDK, all the accounts, keys and existing SDK configuration will be lost, and the SDK will need to be re-activated by enrolling again the relevant user accounts. Also note that before calling an SDK protected function, user presence must be verified using an SDK unlock function.

Installation

We are going to assume that you are using Android Studio for your development. You can install FuturaeKit into your project using Maven.

Maven

The preferred way to get the FuturaeKit SDK for Android is through Github Packages maven repository.

To do so add the following lines to your build.gradle file:

repositories {
  maven {
    url "https://maven.pkg.github.com/Futurae-Technologies/android-sdk" //for stable versions
    credentials {
      username = "$GITHUB_ACTOR"
      password = "$GITHUB_TOKEN"
    }
  }
}

dependencies {
  implementation('com.futurae.sdk:futuraekit:x.x.x')
}

Of course, make sure to specify the desired version number, in place of x.x.x.

Configuration and Initialization

To configure and initialize the SDK, take the following steps:

Add Permissions

Please add the following permissions, which the FuturaeKit SDK needs, if they are not already present in your AndroidManifest.xml file:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

<meta-data
    android:name="com.google.android.gms.vision.DEPENDENCIES"
    android:value="barcode"/>

Initialize the SDK

SDK Configuration Options

The SDK can be configured to provide a fine-tuned mode of operation with regards to user presence verification and security, using the following configuration parameters. For a detailed explanation of how user presence verification works for these configurations, please refer to user presence verification section.

Lock configuration type

setLockConfigurationType() - This function sets the user presence verification lock configuration type between the following options: NONE,BIOMETRICS_ONLY, BIOMETRICS_OR_DEVICE_CREDENTIALS and SDK_PIN_WITH_BIOMETRICS_OPTIONAL

Unlock duration

setUnlockDuration() - Sets the duration in seconds that the SDK remains unlocked for, after successfully calling an unlock method. The accepted value range is between 2 and 300 seconds, and configuring any value out of this range will raise an exception. If the unlock duration is not set, then the SDK will assume a default value of 60 seconds.

Biometric invalidation

setInvalidatedByBiometricChange() - If set to true, any change in the device biometrics settings will result in the user presence verification mechanism being invalidated, rendering most of the SDK functions unavailable depending on the lock configuration type. For a detailed explanation, please refer to user-presence-verification section.

Unlocked device required

setUnlockedDeviceRequired() - if set to true, the device must be unlocked to perform cryptographic operations. Otherwise operations will fail even if the SDK is unlocked. This defaults to false

Skip hardware security

setSkipHardwareSecurity() - if set to true, the SDK will not store cryptographic material on the device’s strongbox. This defaults to false meaning if this is not set, the SDK will always attempt to use the strongbox if available on the device.

SDK Initialization

We recommend using an android Application class to initialize the SDK. If you already have one in your app, follow these steps:

Firstly, in your Application class find or create the onCreate method and add the following code to initialize the FuturaeKit SDK:

import com.futurae.sdk.FuturaeSDK;

class App : Application() {

  override fun onCreate() {
    super.onCreate();

    val config = SDKConfiguration.Builder()
      .setLockConfigurationType(config)
      .setUnlockDuration(unlockDuration)
      .setInvalidatedByBiometricChange(true)
      .setUnlockedDeviceRequired(true)
      .setSkipHardwareSecurity(false)
      .build()
    try {
      //launch SDK with the specified configuration
      FuturaeSDK.launch(this, config)   
    } catch (e : Exception) {
      when(e) {
        is IllegalStateException -> {
          // Indicates that SDK is already initialized. 
        }
        is LockInvalidConfigurationException -> {
          // Indicates that provided SDK configuration is invalid
        }
        is LockMechanismUnavailableException -> {
          // Indicates that provided SDK configuration is valid but cannot be supported on this device
        }
        is LockUnexpectedException -> {
          // Indicates that an SDK cryptographic operation failed}
        }
        is LockCorruptedStateException -> {
          // Indicates that the SDK is in a corrupted state and should attempt to launch for account recovery
          // Keep in mind the SDK is NOT initialized until recovery is completed successfully
          FuturaeSDK.launchAccountRecovery(
            this,
            config,
            object : Callback<Unit> {
              override fun onSuccess(result: Unit) {
                // SDK launched successfully. 
              }

              override fun onError(throwable: Throwable) {
                // Recovery unsuccessful
                if (throwable is RecoveryException) {
                  // Recovery impossible. Use FuturaeSDK.reset() to reset the SDK
                }
              }
            })
        }
      }
    }
}

Once the SDK is initialized, you may access the SDK instance and functions using the SDK getClient() getter:

FuturaeSDK.getClient()

Secondly, in your res/values folder make sure to create a file futurae.xml with the following contents:

<?xml version="1.0" encoding="utf-8"?>
<resources>

  <string name="ftr_sdk_id">{FuturaeSdkId}</string>
  <string name="ftr_sdk_key">{FuturaeSdkKey}</string>
  <string name="ftr_base_url">https://api.futurae.com:443</string>

</resources>

You can get your SDK credentials from Futurae Admin:

  • First you need to choose or determine the Futurae Service which is configured in your backend application.
  • Assuming you can access the particular Service on Futurae Admin, you can go to Settings -> Mobile SDK in order to get your SDK credentials
  • If Mobile SDK is not enabled for the particular Service, please contact Futurae Support in order to enable it.
SDK Initialization errors & recovery

Errors can occur during SDK initialization, either due to SDK misconfiguration or device failures. For a full list of errors, refer to FuturaeSDK.launch.

  • IllegalStateException: this exception during the SDK initialization suggests that the SDK was already initialized. It is recommended to check if this SDK launch method in your app code is called after the SDK is already initialized. In this case you might be able to access the SDK instance with FuturaeSDK.getClient().
  • LockMechanismUnavailableException: This exception indicates that the device does not support strong biometrics. Resetting the SDK will not resolve this issue. This problem may occur on specific devices that do not meet Class 3 (formerly Strong) biometrics specification by Android. In such cases, it is recommended to handle the exception by avoiding biometrics for these devices and using the SDK PIN as the user presence verification mechanism instead. Refer to user presence verification section for all supported options.
  • LockInvalidConfigurationException: This exception typically occurs when the SDK is initialized with an incorrect combination of parameters. In this scenario, it is advised to double-check the SDK configuration with the SDK Configuration Options section.
  • LockUnexpectedException: This exception during initialization means that the Keystore fails to generate key material. The SDK’s setSkipHardwareSecurity feature, supported since version 2.5.0, may help in some of these exception cases.
  • LockCorruptedStateException: This exception occurs when the SDK’s cryptographic material or encrypted storage become corrupted. Some corrupted cases may be recoverable using the SDK’s FuturaeSDK.launchAccountRecovery method, available only for the SDK configuration type NONE. If the SDK cannot recover the corrupted material, it must be reset, and existing accounts will need to be re-enrolled. We are exploring the possibility of extending this recovery functionality to other lock types, such as SDK_PIN_WITH_BIOMETRICS_OPTIONAL. Currently, for other configuration types, the alternative is to reset the SDK and re-enroll the affected accounts.

Configure R8 and ProGuard

For maven installations of the SDK, consumerProguardFiles are used by the SDK to provide the proguard rules automagically to your app. Nothing more needs to be done.

Demo App

The SDK’s repository contains a stripped-down demo app which showcases examples of how some of the SDK’s core features can be integrated. For beta versions, please refer to the SDK beta repository.

Features

The FuturaeKit SDK for Android provides the following features and functionality:

Callbacks

The SDK methods that perform asynchronous operations use callbacks as feedback mechanism. These calls expect an object of the FuturaeCallback or Callback<T> interfaces as an argument:

public interface FuturaeCallback {
  void success();
  void failure(Throwable throwable);
}

interface Callback<T> {

    fun onSuccess(result : T)

    fun onError(throwable: Throwable)

}

Push notifications

Your app must be set up to receive Firebase Cloud Messaging (FCM) push notifications from the Futurae backend. You can choose to receive and handle these notifications yourself, or alternatively you can use the existing infrastructure provided in the SDK. You can find more information on how to setup FCM push notifications for your app in the Firebase Cloud Messaging Developer Guide.

In order to be able to receive FCM notifications, you need to specify the Firebase Messaging service inside the application section of your Manifest:

<service android:name="com.futurae.sdk.messaging.FTRFcmMessagingService">
  <intent-filter>
    <action android:name="com.google.firebase.MESSAGING_EVENT" />
  </intent-filter>
</service>

For this purpose, you can either use the one included in the SDK (com.futurae.sdk.messaging.FTRFcmMessagingService), or write your own. This service overrides two important methods:

@Override
public void onNewToken(String token);

@Override
public void onMessageReceived(RemoteMessage message);

The first one is invoked whenever a new FCM push token has been generated for the app; it is important to register this token with the Futurae backend in order to continue receiving push notifications. The FTRFcmMessagingService implements this functionality.

The second one is invoked whenever a new push notification (or cloud message) is received by the app. The FTRFcmMessagingService then processes this message and invokes the SDK accordingly.

FCM token registration

The FTRFcmMessagingService is responsible for registering the app’s FCM token to the Futurae server. This is important for the server to be able to issue FCM notifications for your app. The provided service handles this, however if you need to, you can write your own or extend the existing one.

If you are implementing your own FCM notification handling, you should register the FCM token to the Futurae server every time it changes. The call that registers the FCM token to the Futurae server is registerPushToken() , and it is necessary every time the FCM token is generated or is changed by FCM.

For example, once the app receives a new FCM token (e.g. via an onNewToken() callback in your own FirebaseMessagingService subclass), the token needs to be obtained and registered to the Futurae server using the following code:

 override fun onNewToken(token: String) {
    super.onNewToken(token)
        
    if(FuturaeSDK.isSDKInitialized) {
        FuturaeSdkWrapper.client.registerPushToken(token, object : FuturaeCallback {
            override fun success() {
                //success
            }

            override fun failure(t: Throwable) {
                // handle failure and schedule re-upload
            }
        })
    }
}

FCM listener service

The FTRFcmMessagingService receives FCM push notifications and handles them, according to the actions dictated by the Futurae server. You can use or extend the service provided by the SDK, or write your own. There are two distinct push notification types issued by the Futurae server: Aprove or Unenroll.

In case you want to process and handle the FCM notifications without using FTRFcmMessagingService, you must use the following code in order to process and handle the notifications sent by the Futurae server, inside the implementation of your own FirebaseMessagingService subclass:

override fun onMessageReceived(message: RemoteMessage) {
    super.onMessageReceived(message)
  
    if(FuturaeSDK.isSDKInitialized) { 
        val messageData = message.data

        // convert map -> bundle
        val data = Bundle()
        messageData.entries.forEach { (k, v) ->
            data.putString(k, v)
        }

        val ftrNotificationFactory = FuturaeSDK.getFTRNotificationFactory()
        val notification = ftrNotificationFactory.createNotification(data)
        notification?.handle() 
    }
}

Local intents

Once a Futurae FCM notification has been handled, the SDK will notify the host app using local broadcasts. The app should register a broadcast receiver for these intents and react accordingly. There are three distinct Intents that the notification handlers might send in a local broadcast:

  • INTENT_GENERIC_NOTIFICATION_ERROR: Indicates that an error was encountered during the processing or handling of a FCM notification.
  • INTENT_APPROVE_AUTH_MESSAGE: Indicates that a One-Touch athentication attempt has been initiated.
  • INTENT_ACCOUNT_UNENROLL_MESSAGE: Indicates that a user account has been logged out remotely.

The following example shows how to register for these intents:

IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(NotificationUtils.INTENT_GENERIC_NOTIFICATION_ERROR);  // General notification error intent
intentFilter.addAction(NotificationUtils.INTENT_APPROVE_AUTH_MESSAGE);        // Approve authentication notification intent
intentFilter.addAction(NotificationUtils.INTENT_ACCOUNT_UNENROLL_MESSAGE);    // Logout user notification intent

LocalBroadcastManager.getInstance(getContext()).registerReceiver(new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {

    switch(intent.getAction()) {
      case NotificationUtils.INTENT_ACCOUNT_UNENROLL_MESSAGE:
        // Handle unenroll notification (e.g. refresh lists)
        break;

      case NotificationUtils.INTENT_APPROVE_AUTH_MESSAGE:
        // Handle approve notification (e.g. show approve view)
        break;

      case NotificationUtils.INTENT_GENERIC_NOTIFICATION_ERROR:
        // Handle FCM notification error
        break;
    }
  }
}, intentFilter);

Encrypted push notification extras

The Futurae Authentication platform provides a mechanism to encrypt the extra information that is provided in the context of a Push authentication or transaction signing session. When encrypted, the sessions’s extra information is encoded as a base64 string of an encrypted JSON.

The SDK function getDecryptedPushNotificationExtras receives a push notification intent produced by the SDK and returns a JSONArray.

 private val broadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        when (intent.action) {
            NotificationUtils.INTENT_APPROVE_AUTH_MESSAGE -> {
                val hasExtraInfo =
                    intent.getBooleanExtra(NotificationUtils.PARAM_HAS_EXTRA_INFO, false)

                val decryptedExtras: JSONArray? = try {
                    FuturaeSDK.client.getDecryptedPushNotificationExtras(intent)
                } catch (e: LockOperationIsLockedException) {
                    Timber.e("Please unlock SDK to get extras: ${e.message}")
                    null
                } catch (e: IllegalStateException) {
                    Timber.e("Some data is missing for decryption: ${e.message}")
                    null
                } catch (e: LockUnexpectedException) {
                    Timber.e("Cryptography error: ${e.message}")
                    null
                }

                // Use extra information
            }
            NotificationUtils.INTENT_ACCOUNT_UNENROLL_MESSAGE -> {
                //...
            }
            NotificationUtils.INTENT_APPROVE_CANCEL_MESSAGE -> {
                //...
            }
            NotificationUtils.INTENT_GENERIC_NOTIFICATION_ERROR -> {
                //...
            }
        }
    }
}

As a protected function, the SDK has to be unlocked before decrypting the extra information content.

User presence verification

User presence verification enhances security by requiring the Futurae SDK to be unlocked by verifying user presence, before certain protected SDK operations can be performed. User presence can be verified by leveraging Class 3 Strong biometrics (e.g., fingerprint, iris, or face recognition), device credentials (e.g. PIN, pattern, or password) or an SDK PIN (also known as Custom App PIN), set by the user upon the first enrollment, with the purpose of verifying the user identity to unlock the SDK methods that are protected by the user presence verification mechanism.

The Futurae SDK provides a set of options which can be configured during the Initialize the SDK step, in order to control the behavior of the user presence verification mechanism.

setLockConfigurationType() - This function sets the user presence verification lock configuration type between the following options:

  • NONE - The SDK will not use any lock mechanism, therefore user presence verification will not be enforced. This is the default value in case that a lock configuration type is not set. This option must be used when launching the SDK for the first time, after migrating from a previous SDK v1.x.x install.
  • BIOMETRICS_ONLY - This lock configuration type allows the end user to only unlock the SDK through strong biometric authentication.
  • BIOMETRICS_OR_DEVICE_CREDENTIALS - The SDK can be unlocked by authenticating either using biometrics or device credentials.
  • SDK_PIN_WITH_BIOMETRICS_OPTIONAL - Allows the user to perform user presence verification by authenticating with an SDK-specific PIN or with biometrics. In this case, the user is required to set an SDK PIN (upon the first enrollment), which will be stored and verified on the server side. On top of that, the user may choose to enable and use biometric authentication, as a means of convenience. This SDK configuration implies a server-side validation of the SDK PIN, even when using biometric verification, therefore the device must have network connectivity. Nevertheless, when configured with this lock configuration type, the SDK provides functionality which can be used to authenticate the user even when the device is offline, such as the methods describe in the sections TOTP and offline QR code.

setUnlockDuration() - Sets the duration in seconds that the SDK remains unlocked for, after successfully calling an unlock method. The accepted value range is between 2 and 300 seconds, and configuring any value out of this range will raise an exception. If the unlock duration is not set, then the SDK will assume a default value of 60 seconds.

setInvalidatedByBiometricChange() - If set to true, any change in the device biometrics settings will result in the user presence verification mechanism being invalidated, rendering most of the SDK functions unavailable. The steps to reinstate the user presence verification mechanism vary according to the configured lock configuration type:

  • NONE - doesn’t support user presence verification.
  • SDK_PIN_WITH_BIOMETRICS_OPTIONAL - The app can either re-activate biometrics or reset and initialize the SDK again.
  • For the remaning lock configuration types, the only option is to reset and initialize the SDK.

Verify user presence with biometrics

The Futurae SDK can be unlocked by performing biometrics verification. After calling the unlockWithBiometrics() method, the OS will display a biometric prompt to authenticate the user and, if the verification is successful, the SDK will be unlocked for the configured period.

The unlockWithBiometrics method can be used when the SDK configured lock mechanism type is BIOMETRICS_ONLY or SDK_PIN_WITH_BIOMETRICS_OPTIONAL. When the SDK configured lock mechanism is SDK_PIN_WITH_BIOMETRICS_OPTIONAL, a server-side verification of the SDK PIN takes place, therefore network connectivity is required. Upon succesful unlock, the SDK will return the type of UserPresenceVerification indicating the way user-presence was verified.

FuturaeSDK.getClient().unlockWithBiometrics(
  //the activity that will present the Biometrics prompt to the user
  requireActivity(),

  //the title used of the Biometrics prompt
  "Unlock SDK",

  //the subtitle used for the Biometrics prompt
  "Authenticate with biometrics",
  
  //the description used for the Biometrics prompt 
  "Authentication is required to unlock SDK operations",
  
  //the text for the “cancel” button of the Biometrics prompt
  "cancel",
  
  //callback used to receive the response of the unlock request
  object : Callback<UserPresenceVerification> {
    override fun onSuccess(type: UserPresenceVerification) {
      //user presence successfully verified
    }
    override fun onError(throwable: Throwable) {
      //user presence verification failed
      showErrorAlert("SDK Unlock", throwable)
    }
 }
)

Verify user presence with biometrics or device credentials

if the SDK setLockConfigurationType is set as BIOMETRICS_OR_DEVICE_CREDENTIALS, user presence can be verified using biometrics, providing that the SDK biometrics verification is active. If biometrics verification is not active, the SDK will automatically proceed with user presence verification using the configured device credentials, such as the device PIN.

The method unlockWithBiometricsDeviceCredentials() will attempt to verify user presence preferentially using biometrics, when the conditions described in the section SDK biometrics configuration are met, or alternatively using the configured device credentials. Upon succesful unlock, the SDK will return the type of UserPresenceVerification indicating the way user-presence was verified.

FuturaeSDK.getClient().unlockWithBiometricsDeviceCredentials(
  //the activity that will present the authentication prompt to the user
  requireActivity(),

  //the title used of the authentication prompt
  "Unlock SDK",

  //the subtitle used for the authentication prompt
  "Authenticate with biometrics or device credentials",
  
  //the description used for the authentication prompt 
  "Authentication is required to unlock SDK operations",
  
  //callback used to receive the response of the unlock request
  object : Callback<UserPresenceVerification> {
    override fun onSuccess(type: UserPresenceVerification) {
      //user presence successfully verified
    }
    override fun onError(throwable: Throwable) {
      //user presence verification failed
      showErrorAlert("SDK Unlock", throwable)
    }
  }
)

Verify user presence with SDK PIN or biometrics

User presence can also be verified using an SDK PIN, which is an alphanumeric password that the user configures in the app by the time that the first account is activated, and it may then be used as a user presence verification mechanism.

In this case, the app needs to implement the relevant mechanism to ask the user to supply the SDK PIN and then invoke the function unlockWithSDKPin() by providing the PIN supplied by the user. After successfully verifying the SDK PIN, the SDK will remain unlocked for the configured unlock period.

Unlocking the SDK with an SDK PIN is only available if the configured lock configuration type is SDK_PIN_WITH_BIOMETRICS_OPTIONAL and after the user complete the first enrollment and setup the SDK PIN.

This unlock method requires a server-side verification of the SDK PIN, therefore the device must have network connectivity for the operation to be completed successfully. Moreover, the user can choose to enable the SDK biometrics authentication, as a means of convenience. When the SDK biometrics verification is enabled, user presence can be additionally verified using biometrics, besides using the SDK PIN. Upon succesful unlock, the SDK will return the type of UserPresenceVerification indicating the way user-presence was verified.

FuturaeSDK.getClient().unlockWithSDKPin(
  //the SDK PIN inserted by the user
  sdkPin,

  //callback used to receive the response of the unlock request
  object : Callback<Unit> {
    override fun onSuccess(type: UserPresenceVerification) {
      //user presence successfully verified
    }
    override fun onError(throwable: Throwable) {
      //user presence verification failed
      showErrorAlert("SDK Unlock", throwable)
    }
  }
)
SDK PIN entry constraints

As the SDK PIN is verified by the Futurae backend, the constraints described below are imposed on the server side.

  1. The SDK PIN validation is limited to 5 consecutive failed attempts, and the at the 5th consecutive failed attempt the SDK will enter a permanent locked state.

  2. The failed SDK PIN entry attempts are consecutive. A user may enter a wrong pin 4 times without consequences. If the correct SDK PIN is entered during the 5th attempt, the failed attempts counter is reset to 0.

  3. After a successful SDK PIN validation, the SDK PIN failed attempts counter is reset to 0.

  4. The SDK does not enforce specific SDK PIN patterns or restrictions, since the PIN policies can be enforced by the host app.

SDK locked due to consecutive SDK PIN failed attempts

When the SDK is locked due to failed SDK PIN attempts:

  • The SDK is permanently locked and cannot be used again.
  • The user device in the Futurae backend is archived and cannot be used to authenticate the user anymore.
  • Once locked due to failed SDK PIN attempts, the SDK will need to be reset.
  • The user will need to re-enroll all previously enrolled accounts.

The following SDK exceptions related to SDK PIN can be caught and handled by the hosting app:

  • FTApiPinNotNeededException
    • With error 40000: When the SDK PIN is provided but not required.
  • FTApiPinMissingException
    • With error 40061: Indicates that the SDK PIN is missing or empty.
  • FTApiPinIncorrectException
    • With error 40164: If the user enters an incorrect SDK PIN for the 1st time, this code will be triggered. It also means that there are 4 more attempts left to enter the correct SDK PIN.
    • With error 40163: When the user enters an incorrect SDK PIN for the 2nd time. It also informs that there are 3 more attempts left to enter the correct SDK PIN.
    • With error 40162: When the user enters an incorrect SDK PIN for the 3rd time. It also informs that there are 2 more attempts left to enter the correct SDK PIN.
    • With error 40161: When the user enters an incorrect SDK PIN for the 4th time. It also informs that there is 1 more attempt left to enter the correct SDK PIN.
  • FTApiDeviceArchivedException
    • With error 41002: When the SDK PIN validation fails for the 5th consecutive time, thus the SDK is permanently locked.

Lock SDK

It is advised that the app locks the SDK after an operation finishes as well as when the app is in a state where it would have its security compromised, for instance when it is moved to the background. The SDK may be immediately locked by calling the lock() function.

FuturaeSDK.getClient().lock()

Performing a successful user verification will unlock the SDK for the configured unlockDuration passed as an argument during initialization. It’s recommended that the app locks the SDK right after the completing the protected operation to which it was unlocked for, instead of waiting the configure unlockDuration to expire leading the SDK to be automatically locked.

Once locked, the SDK will need to be unlocked again via user presence verification as described above and depending on the selected lock configuration type.

Check if the SDK is locked

At any point in time you can check the locked status of the SDK by calling the method isLocked().

if (FuturaeSDK.getClient().isLocked()) {
  //Unlock SDK before calling a protected method
}

Get SDK unlock methods

The SDK provides functionality for the app to query the available methods which can be used to perform user verfication and consequently unlock the SDK. Calling the method getActiveUnlockMethods returns a list of UnlockMethodType enum values that the SDK can currently be unlocked with:

FuturaeSDK.getClient().activeUnlockMethods.joinToString()

enum UnlockMethodType {
     BIOMETRICS,
     BIOMETRICS_OR_DEVICE_CREDENTIALS,
     SDK_PIN
}

Switch SDK configuration

The SDK provides a way to switch to a different SDK configuration post initialization via the following methods.

QR codes

The Futurae Authentication platform, uses QR codes for different operations:

  • Enroll a device for a user (online)
  • Authenticate a user or transaction (online)
  • Authenticate a user or transaction (offline)
  • Approve or reject an authentication session not attached to a specific user (usernameless)

The Futurae SDK supports all these functionalities. The business App can have different flows for supporting the needed functionality, or condense all QR Code operations in one. The Futurae SDK provides a handy method to identify on the fly which type of QR Code the user has scanned:

Barcode qrcode = data.getParcelableExtra(FTRQRCodeActivity.PARAM_BARCODE);
switch (FuturaeClient.getQRCodeType(qrcode.rawValue)) {
  case FuturaeClient.QR_ENROLL:
    // handle Enroll QR Code
    break;
  case FuturaeClient.QR_ONLINE:
    // handle Online QR Code Factor
    break;
  case FuturaeClient.QR_OFFLINE:
    // handle Offline QR Code Factor
    break;
  case FuturaeClient.QR_USERNAMELESS:
    // handle usernameless QR code
    break;

}
when (QRCodeUtils.getQrcodeType(qrcodeValue)) {
    QRCodeUtils.QRType.Enroll -> // enroll
    QRCodeUtils.QRType.Offline -> // compute verification code from QR
    QRCodeUtils.QRType.Online -> // fetch session and approve
    QRCodeUtils.QRType.Usernameless -> // select an account, fetch session and approve
}

Enroll

To enroll this SDK as a device for a Futurae User, there are two options. Enrollment via an activation QR code and enrollment via an activation shortcode.

Enroll with QR code

To enroll with an enrollment QR code, call the enroll() method using a valid activation code, for example obtained by scanning an enrollment QR Code:

startActivityForResult(FTRQRCodeActivity.getIntent(this), FTRQRCodeActivity.RESULT_BARCODE);

If a QR Code is successfully scanned then onActivityResult will be called, with the scanned code:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {

  if (resultCode == RESULT_OK && requestCode == FTRQRCodeActivity.RESULT_BARCODE) {

    //you may use this or any other function to scan the QR code
    Barcode qrcode = data.getParcelableExtra(FTRQRCodeActivity.PARAM_BARCODE);

    //first, make sure the SDK is unlocked, then proceed

    FuturaeSDK.getClient().enroll(qrcode.rawValue, new FuturaeCallback() {
      @Override
      public void success() {

      }

      @Override
      public void failure(Throwable throwable) {

      }
    });
  }

}

Please make sure to call enroll, inside the onActivityResult method to complete the user enrollment.

Enroll with activation shortcode

To enroll with an activation shortcode, call the protected method enrollWithShortcode() using a valid activation code:

FuturaeSDK.getClient().enrollWithShortcode(shortcode, new FuturaeCallback() {
      @Override
      public void success() {

      }

      @Override
      public void failure(Throwable throwable) {

      }
});

Enroll and setup the SDK PIN

When the SDK lock configuration type is SDK_PIN_WITH_BIOMETRICS_OPTIONAL, the user must set the SDK PIN upon the first enrollment, therefore the activation of the first account is perfomed by calling the SDK methods enrollAndSetupSDKPin or enrollWithShortcodeAndSetupSDKPin instead, and providing as argument the PIN inserted by the user.

The app must implement the necessary mechanism to ask the user to insert the SDK PIN. Currently Futurae does not perform any validation on the quality of the supplied PIN. The app is assumed to ensure that the PIN meets the security standards as defined by the customer app logic and requirements (for example: PIN “1111” or “1234” is not allowed).

FuturaeSDK.getClient().enrollAndSetupSDKPin(
  //qrcode raw value
  scannedCode,
  //sdkpin set by the user
  sdkPin,
  //callbacks
  object : Callback<Unit> {

    override fun onSuccess(result: Unit) {
      //the account was enrolled and the SDK is unlocked
    }
    
    override fun onError(throwable: Throwable) {
      //handle enrollment failure
    }
  }) 

Logout (Unenroll)

To remove an account from this SDK (ie unenroll this user device), call the protected method logout():

//first, make sure the SDK is unlocked, then proceed

FuturaeSDK.getClient().logout(userId, new FuturaeCallback() {
  @Override
  public void success() {
    //handle logout success (eg. refresh accounts list)
  }

  @Override
  public void failure(Throwable throwable) {
    //handle logout failure
  }
});

Typically this should happen either when the user removes the account manually from the app, or when a user has been logged out remotely by the server. In the former case, calling the logout() method is enough, as it notifies the server of the logout and deletes the account from the SDK too. In the latter case, the server will send a FCM notification to the app, and the default handler of the notification will delete the account from the SDK as well. In this case, the notification handler will also send a local broadcast to the app (see INTENT_ACCOUNT_UNENROLL_MESSAGE above, so that the app can also perform any further action required (e.g. refresh the list of active accounts in an account view).

The intent contains the Futurae user_id, as well as the device_id, as an extra:

String userId = intent.getStringExtra(NotificationUtils.PARAM_USER_ID);
String deviceId = intent.getStringExtra(NotificationUtils.PARAM_DEVICE_ID);

To manually delete an account from the local database, which is needed for instance if you handle the FCM notifications without using FTRFcmMessagingService (see the Push notifications section) and an unenroll push notification is received, call the deleteAccount() method:

FuturaeSDK.getClient().deleteAccount(userId)

SDK biometrics configuration

The Futurae SDK allows the user to configure the device biometrics as a convenient way to verify the user presence and unlock the SDK. Once biometrics verification is enabled, it will become the first choice to perform the user presence verification.

The SDK may be unlocked using biometrics, given that the following conditions are met:

  • The SDK is configured with LockConfigurationType.BIOMETRICS_OR_DEVICE_CREDENTIALS, LockConfigurationType.SDK_PIN_WITH_BIOMETRICS_OPTIONAL or LockConfigurationType.BIOMETRICS_ONLY (in this last case, biometrics verification is mandatory as it is the only method available to unlock the SDK).
  • If the SDK is configured with LockConfigurationType.SDK_PIN_WITH_BIOMETRICS_OPTIONAL, then biometrics verification must be activated at the SDK level. To do so, the function activateBiometrics() must be called. Not that only for this lock configuration type the app needs to activate biometrics verification at the SDK level. For all the other lock configuration types, biometrics verification will automatically be used to unlock the SDK, as long the user has biometrics configured as a lock mechanism to unlock the device.
  • The SDK biometric keys are valid. If the SDK configuration setInvalidatedByBiometricChange is set as true, in order for the SDK keys to be valid the following conditions must be verified:
    • No new biometrics were configured. If the user configures a new biometric entry in the device settings, the SDK biometrics will become invalid.
    • At least one of the biometrics remain configured since the last time that the SDK was initiatilized or the SDK biometrics were activated.
  • The user has at least one biometric verification option configured in the device settings.

Activate SDK biometrics verification

To activate the SDK biometrics verification the app needs to call the protected function activateBiometrics(), after verifying the user presence to unlock the SDK.

The activateBiometrics method is only available for the SDK_PIN_WITH_BIOMETRICS_OPTIONAL lock configuration type. If called right after enrolling the first account and setting up the SDK PIN with the method enrollAndSetupSDKPin, and before the unlockDuration expires, the app may call this function without having to unlock the SDK. This is particularly useful for apps that want to enforce biometrics as a user presence verification method.

The following example may be used for instance when the user enables biometrics verification in the app settings:

//first, make sure the SDK is unlocked with SDK PIN verification, then proceed
FuturaeSDK.getClient().activateBiometrics(

  //Activity required for the biometric authentication.
  requireActivity(),

  //Title used for the authentication prompt.
  "Unlock SDK",

  //Subtitle used for the authentication prompt.
  "Activate biometrics",

  //Description used for the authentication prompt.
  "Authenticate to enable biometric authentication",

  //Text for the “cancel” button of the Biometrics prompt.
  "cancel",

  //Callback to receive the result or error of this operation.
  object : Callback<Unit> {
    override fun onSuccess(result: Unit) {
      showAlert("SDK Unlock", "Biometric auth activated")
      //The SDK biometrics are enabled.
    }

    override fun onError(throwable: Throwable) {
      showErrorAlert("SDK Unlock", throwable)
      //Handle biometric activation failure.
    }

  }
)

Deactivate SDK biometrics verification

If the user wishes to deactivate biometrics verification, the app may use the protected deactivateBiometrics() function to revoke biometric authentication as an option to unlock the SDK.

Being a protected function, deactivateBiometrics may only be called after verifying the user presence using SDK PIN.

//first, make sure the SDK is unlocked with SDK PIN verification, then proceed
FuturaeSDK.getClient().deactivateBiometrics(
  
  //callback to receive the result or error of this operation.
  object : Callback<Unit> {

    override fun onSuccess(result: Unit) {
      //handle successful biometrics deactivation
    }

    override fun onError(throwable: Throwable) {
      //handle failed biometrics deactivation
    }

})

Check if the SDK biometrics are valid

Specifically when the SDK is configured with setInvalidatedByBiometricChange(true), if new biometric credentials are configured in the device or in case that the user removes all the configured biometrics credentials, the SDK biometric verification will be automatically revoked and the user will have to explicitly activate it again using activateBiometrics as described above or reset the SDK if the SDK is configured with LockConfigurationType is BIOMETRICS or BIOMETRICS_OR_DEVICE_CREDENTIALS.

To check if the device biometric credentials have changed, the app may call the haveBiometricsChanged() method:

if (FuturaeSDK.getClient().haveBiometricsChanged()){
  // the device biometrics have changed, to reactivate call activateBiometrics()
}

Change SDK PIN

When the SDK LockConfigurationType is SDK_PIN_WITH_BIOMETRICS_OPTIONAL, the SDK PIN configured upon the first enrollment, may be updated with the protected function changeSDKPin() as shown next:

//first, make sure the SDK is unlocked with SDK PIN verification, then proceed
FuturaeSDK.getClient().changeSDKPin(it, object : Callback<Unit> {
  override fun onSuccess(result: Unit) {
    //handle successful SDK PIN change
  }

  override fun onError(throwable: Throwable) {
    //handle failed SDK PIN change
  }
})

Account status

To get a list of all enrolled accounts in this SDK, call the getAccounts() method:

List<FTAccount> accounts = FuturaeSDK.getClient().getAccounts();

In order to get a specific FTAccount, call the functions getFTAccountByUserId() or getAccountByUserIdAndDeviceId():


//get the FTAccount for the provided userId
FTAccount account = FuturaeSDK.getClient().getFTAccountByUserId(userId);

//get the FTAccount for the provided userId and deviceId
FTAccount account = FuturaeSDK.getClient().getAccountByUserIdAndDeviceId(userId, deviceId);

To fetch the status and pending authentication Authentication session information for these accounts, you can use the following method (where userIds is a list of the corresponding user IDs):

FuturaeSDK.getClient().getAccountsStatus(userIds, new FuturaeResultCallback<AccountsStatus>() {
  @Override
  public void success(AccountsStatus accountsStatus) {
    // Handle the pending sessions
  }

  @Override
  public void failure(Throwable throwable) {
    // Handle the error
  }
});

Account History

Protected function to get a list of all authentications for an enrolled account in the SDK, call the getAccountHistory method:

FuturaeSDK.getClient().getAccountHistory(String userId, Callback<List<AccountHistory>> callback);

Authenticate user

One-Touch

When a One-Touch (aka Approve) session starts, the server will send a push notification to the app, where the user should approve or reject the authentication session. The notification is received and handled by the SDK, which in turn will send a local broadcast (see INTENT_APPROVE_AUTH_MESSAGE above), so that the app can perform any required actions. For example, the app might want to display a prompt to the user so that they can approve or reject the session.

The intent contains an object that describes the authentication session as an extra. This object contains the user ID and the session ID, which are required for sending the authentication outcome to the server:

ApproveSession authSession = intent.getParcelableExtra(NotificationUtils.PARAM_APPROVE_SESSION);
String userId = authSession.getUserId();
String sessionId = authSession.getSessionId();

Once the outcome of the authentication has been decided by the app, it should be sent to the server for the authentication session to complete.

Approve authentication

To approve the authentication session, when no extra_info is supplied in Authenticate with One-Touch, use the approveAuth() method. This is a protected method so the SDK must be first unlocked according to the configured SDK lockConfigurationType.

The following example demonstrates how to approve an authentication session when the SDK is unlocked by verifying user presence using biometrics:

FuturaeSDK.getClient().unlockWithBiometrics( //use the approriate unlock function
  requireActivity(),
  "Unlock SDK",
  "Authenticate with biometrics",
  "Authentication is required to unlock SDK operations",
  "cancel",
  object : Callback<Unit> {
    override fun onSuccess(result: Unit) {
      FuturaeSDK.approveAuth(
        //Futurae account's user id which the authentication was created for
        userId
        
        //session identifier returned by the Futurae backend Auth API
        sessionId

        //Callback to handle the success or failure of the approve operation
        object : Callback<Unit> {

          override fun onSuccess(result: Unit) {
            //handle session successful approved
          }

          override fun onError(throwable: Throwable) {
            //handle session approve failure
            showErrorAlert("Failed to approve session", throwable)
          }
        })
    }
    override fun onError(throwable: Throwable) {
      showErrorAlert("SDK Unlock failure", throwable)
    }
  }
)
Approve with Multi-Numbered Challenge

If Multi-Numbered Challenge is enabled for your Futurae Service, SessionInfo objects returned by the sessionInfoById method will have multiNumberedChallenge field populated with a selection of numbers for the user to choose.

FuturaeSDK.getClient().sessionInfoById(userId, sessionId,
    object:FuturaeResultCallback<SessionInfo> {

        override fun success(result: SessionInfo) {
            // present user with a number selection from the result.multiNumberChoiceList
            // The user will know which one is correct, 
            // because it's shown on the application where transaction was initialized.

            val usersSelectedChoice = 42 // selected by the user

            FuturaeSDK.getClient().approveAuth(
                userId, 
                sessionId, 
                multiNumberedChallengeResponse = usersSelectedChoice, 
                // handle the result in callback
            )
        }

        override fun failure(throwable: Throwable) {
            // ...
        }
    }
)

It is up to the client application to present user with a choice of numbers. User’s choice has to be passed back to the approveAuth method. To approve a user authentication with multi-numbered challange when extraInfo field has a non-null value (refer to authentication session information), use the corresponding approveAuth method overload.

Backend installation receives the correct choice in the response to the authentication call. The correct choice is unknown (not sent) to the SDK. To let user know of the correct choice, it has to be displayed on the application where transaction was initialized.

Reject authentication

The user might choose to reject the authentication session. Additionally, in case the session has not been initiated by the user, they might also choose to report a fraudulent authentication attempt back to the server. In this case, and when no extra_info is supplied in Authenticate with One-Touch, use the following example to unlock the SDK, as this is a protected function, and reject an authentication with the rejectAuth() method:

FuturaeSDK.getClient().unlockWithSDKPin( //use the approriate unlock function
  sdkPin,
  object : Callback<Unit> {
    override fun onSuccess(result: Unit) {
      FuturaeSDK.rejectAuth(
        //Futurae account's user id which the authentication was created for
        userId
        
        //session identifier returned by the Futurae backend Auth API
        sessionId

        //boolean flag to choose whether to report a fraudulent authentication attempt
        reportFraud

        //Callback to handle the success or failure of the reject operation
        object : Callback<Unit> {

          override fun onSuccess(result: Unit) {
            //handle session successful rejected
          }

          override fun onError(throwable: Throwable) {
            //handle session reject failure
            showErrorAlert("Failed to reject session", throwable)
          }
        })
    }
    override fun onError(throwable: Throwable) {
      showErrorAlert("SDK Unlock failure", throwable)
    }
  }
)

Online QR code

The online QR Code authentication factor consists in using the authenticator app to scan a QR Code provided by the server.

To complete the authentication, provide the content of the online QR Code scanned by the user to the protected function approveAuth(), according to the following example:

FuturaeSDK.getClient().unlockWithSDKPin( //use the approriate unlock function
  sdkPin,
  object : Callback<Unit> {
    override fun onSuccess(result: Unit) {
      FuturaeSDK.approveAuth(
        //raw value parsed from the scanned QR code.
        qrCode

        //Callback to handle the success or failure of the approve operation
        object : Callback<Unit> {

          override fun onSuccess(result: Unit) {
            //handle session successful approved
          }

          override fun onError(throwable: Throwable) {
            //handle session approve failure
            showErrorAlert("Failed to approve session", throwable)
          }
        })
    }
    override fun onError(throwable: Throwable) {
      showErrorAlert("SDK Unlock failure", throwable)
    }
  }
)

Offline QR code

Offline QR code allows the user to authenticate by scanning a QR code, without the need of an internet connection on the authenticator device.

To authenticate with the offline QR code Factor, scan the QR code provided by the server and display the relevant information to the user. If the user approves the authentication or transaction request, the app may use the following methods to generate the 6-digit verification code that the user needs to enter in the browser.

In the case of the SDK_PIN_WITH_BIOMETRICS_OPTIONAL lock configuration type, since the SDK PIN is being validated server side, the app needs to act differently on functions that can take place while offline. Specifically, computeVerificationCodeFromQRCode is a protected function that requires prior unlocking of the SDK. However, for the case that the app is offline the SDK offers the following equivalent function that are not protected but require either passing the SDK PIN as argument or if biometrics are activated then the computeVerificationCodeFromQRCodeWithBiometrics method shall be used.

The method computeVerificationCodeFromQrcode() generates the verification code by passing the SDK PIN as argument:

//provide the sdkPin inserted by the user to generate the OTP
result = FuturaeSDK.getClient().computeVerificationCodeFromQrcode(qrCode,sdkPin)
//display the $result (6 digit offline code) to the user

To generate the verification code when the biometrics verification is activated, call the computeVerificationCodeFromQrCodeWithBiometrics() method, which will initiate the user biometrics verification prompt, and then compute the code based on the biometrics result:

FuturaeSDK.getClient().computeVerificationCodeFromQrCodeWithBiometrics(
  qrCode,
  requireActivity(),
  "Unlock SDK",
  "Authenticate with biometrics",
  "Authentication is required to unlock SDK operations",
  "cancel",
  object : Callback<Unit> {
    override fun onSuccess(result: String) {
      //display the $result (verification code) to the user
    }
    override fun onError(throwable: Throwable) {
      showErrorAlert("Failed to generate offline authentication code", throwable)
    }
  }
)

Finally, the verification code can be computed by passing only the QR code data to the computeVerificationCodeFromQrcode method. Nevertheless, user presence has to be verified before that:

//first, make sure the SDK is unlocked, then proceed
FuturaeSDK.getClient().computeVerificationCodeFromQrcode(qrCodeString);

IMPORTANT: In case the backend API Authenticate with Offline QR code (or Authenticate Transaction with Offline QR code) is invoked with the extra_info input parameter (for example, when contextual information for logins, or transaction details for transaction authentication), which includes information that must be displayed to the user before they can approve/reject the request, then you can retrieve this information from the QR code itself, as shown below:

ApproveInfo[] extraInformation = FuturaeClient.getExtraInfoFromOfflineQrcode(qrCode);

Usernameless QR Code

To approve or reject a usernameless QR code session, first scan the QR Code provided by the server, then fetch the session info via the QR Code’s token and finally approve or reject it with an enrolled user’s id using the protected methods approveAuthWithUsernamelessQrCode() or rejectAuthWithUsernamelessQrCode()

FuturaeSDK.getClient().sessionInfoByToken(userId, qrCodeString, new FuturaeCallback<SessionInfo>() {
  @Override
  public void success(SessionInfo sessionInfo) {
      // proceed with approving or rejecting this session
      FuturaeSDK.getClient().approveAuthWithUsernamelessQrCode(
          qrCodeString,
          userId,
          sessionInfo.info,
          new Callback<Unit> {
            @Override
            public void onSuccess(Unit unit) {
                // approved
            }

            @Override
            public void onError(Throwable throwable) {
                // handle error
            }
      });
  }

  @Override
  public void failure(Throwable throwable) {
      // handle error
  }
});

Authentication session information

For a given authentication session, either identified via a session ID (e.g. received by a push notification) or a session Token (e.g. received by a QRCode scan), you can ask the Futurae backend for more information about the session, using the protected methods sessionInfoById() or sessionInfoByToken() respectively:

//first, make sure the SDK is unlocked, then proceed

// if you have a session ID
FuturaeSDK.getClient().sessionInfoById(
  userId, 
  sessionId, 
  object : FuturaeResultCallback<SessionInfo> {
    override fun success(sessionInfo: SessionInfo) {
      // Handle the session
    }

    override fun failure(t: Throwable) {
      // Handle the error
    }
  }
);

// if you have a session Token
FuturaeSDK.getClient().sessionInfoByToken(
  userId, 
  sessionToken, 
  object : FuturaeResultCallback<SessionInfo?> {
    override fun success(sessionInfo: SessionInfo?) {
      // Handle the session
    }

    override fun failure(t: Throwable) {
      // Handle the error
    }
  }
);

If there is extra information to be displayed to the user (supplied when calling Authenticate User or Authenticate Transaction in the backend API), for example when confirming a transaction, this will be indicated with a list of key-value pairs in the extra_info part of the response.

In order to query the server for the authentication session information, you need the user ID and the session ID or session token. You can use the following helper methods to obtain these from either a URI or a QR Code:

public class FuturaeClient {
  public static String getUserIdFromQrcode(String qrCode);
  public static String getSessionTokenFromQrcode(String qrCode);
  public static String getUserIdFromUri(String uri);
  public static String getSessionTokenFromUri(String uri);
}
Authentication with extra information

In case it exists, you can retrieve the extra information that the user needs to review while authenticating by calling the getApproveInfo() method on the SessionInfo object:

public ApproveInfo[] getApproveInfo();

Each ApproveInfo object is a key-value pair that describes a single piece of information; call the following methods to retrieve the key and value of the object respectively.

public String getKey();
public String getValue();

IMPORTANT: In case the authentication contains extra_info, it is mandatory that the information is fetched from the server, displayed to the user, and included in the authentication response in order for it to be included in the response signature. Otherwise, the server will reject the authentication response. Use the following methods to send an authentication response including the authentication information:

class FuturaeClient {
  public void approveAuth(String qrCode, final FuturaeCallback callback, ApproveInfo[] extraInfo);
  public void approveAuth(String userId, String sessionId, final FuturaeCallback callback, ApproveInfo[] extraInfo);
  public void rejectAuth(String userId, String sessionId, boolean reportFraud, final FuturaeCallback callback, ApproveInfo[] extraInfo);
}

TOTP

TOTP authentication can be used for offline authentication, as there is no requirement for an internet connection in the app.

The protected method nextTopt() is used to get the current TOTP, when the SDK LockConfigurationType is other than SDK_PIN_WITH_BIOMETRICS_OPTIONAL:

//first, make sure the SDK is unlocked, then proceed

CurrentTotp totp = FuturaeSDK.getClient().nextTotp(userId);

// The TOTP that the user should use to authenticate
String passcode = totp.getPasscode();

// The remaining seconds of validity of this TOTP
int remainingSeconds = totp.getRemainingSecs();

As seen in this example, the nextTotp() method returns an object that contains the TOTP itself, but also the remaining seconds that this TOTP will be still valid for. After this time, a new TOTP must be obtained by the SDK.

When the SDK LockConfigurationType is SDK_PIN_WITH_BIOMETRICS_OPTIONAL, and the user has no biometrics verification enabled, the user presence is then validated using the SDK PIN. In that case the nexTotp method may be used to authenticate when the user has no internet connection, by adding the the SDK PIN argument:

CurrentTotp totp = FuturaeSDK.getClient().nextTotp(userId, sdkPin);

When the SDK is configured with SDK_PIN_WITH_BIOMETRICS_OPTIONAL, if biometrics verification is activated, the TOTP may be generated using the nextTotpWithBiometrics() method:

FuturaeSDK.getClient().nextTotpWithBiometrics(
  userId,
  requireActivity(),
  "Unlock SDK",
  "Authenticate with biometrics",
  "Authentication is required to unlock SDK operations",
  "cancel",
  object : Callback<Unit> {
    override fun onSuccess(result: CurrentTotp) {
      //display the $result (TOTP) to the user
    }
    override fun onError(throwable: Throwable) {
      showErrorAlert("Failed to generate offline authentication code", throwable)
    }
  }
)

Synchronous Authentication Token

Protected function that creates an OTP based authentication token that can be used with the Authenticate with Synchronous Auth endpoint for synchronous authentication. To get a sychronous authentication token for an enrolled user, use the getSynchronousAuthToken method:

FuturaeSDK.getClient().getSynchronousAuthToken("userId");

As a result of the above method, the mobile SDK will return a synchronous authentication token that can be used to authenticate the user. The token is returned to the mobile app, that has to submit it to the customer backend, which in turn needs to provide the token to the Authenticate with Synchronous Auth Auth API endpoint, in order to authenticate the user.

Users already enrolled will also be able to perform synchronous authentication. After migrating to an SDK version that supports this factor, once the app is launched, the SDK will add the sync factor to the device’s capabilities, provided that the device has stable internet connection, otherwise if it fails to update the sync capability the SDK will try again upon each launch until a successful update.

URI schemes

The SDK is able to handle URI scheme calls, which can be used to either enroll or authenticate users. Once your activity has been set up to handle the URI scheme call intents, get the intent data in the onCreate() method of your activity, which contains the URI that should be passed in the SDK, using the protected method handleUri():

//first, make sure the SDK is unlocked, then proceed

FuturaeSDK.getClient().handleUri(
  uriString, 
  object : FuturaeCallback() {
    override fun success() {
      //handle success
    }

    override fun failure(throwable: Throwable) {
      //handle failure
    }
});

IMPORTANT: In case you perform authentication with a URI scheme call (using the mobile_auth_uri value which is returned by the Futurae backend when calling Authenticate User or Authenticate Transaction), and this authentication includes extra information to be displayed to the user, you must retrieve this information from the server and include it in the authentication response. See section Authentication session information for details on how to do that.

Automatic Account Recovery

Automatic account recovery (aka account migration) allows the user to migrate the Futurae account(s) currently enrolled in an existing Futurae Android SDK installation to a different device, or to the same one after erasing and reinstalling an app. This mechanism migrates the accounts using data which were backed up by the Android OS when the previous app installation had enrolled account(s). Hence, it enables the user to recover their accounts on a new app installation automatically.

On the fresh app installation, the SDK is able to identify if account migration is possible and return the accounts which can be migrated. In order to do so, invoke the method checkForMigratableAccounts() or checkAccountMigrationPossible() for SDK versions previous to 2.1.0:

FuturaeSDK.getClient().checkForMigratableAccounts(object : Callback<MigrateableAccounts> {
            override fun onSuccess(result: MigrateableAccounts) {
                if (result.pinProtected || activeSDKConfiguration.lockConfigurationType == SDK_PIN_WITH_BIOMETRICS_OPTIONAL) {
                    //Proceed with FuturaeSDK.INSANCE.getClient().executeAccountMigrationWithSDKPin
                } else {
                    //Proceed with FuturaeSDK.INSANCE.getClient().executeAccountMigration
                }
            }

            override fun onError(throwable: Throwable) {
                //handle error
            }
});

To execute account migration invoke the executeAccountMigration() or executeAccountMigrationWithSDKPin() methods, depending on the result of the check operation above and your active SDK configuration’s LockConfigurationType. The migrated accounts (i.e., Futurae user ids) will be returned, if successful.

FuturaeSDK.getClient().executeAccountMigration(it,
                object : Callback<List<AccountsMigrationResource.MigrationAccount>> {
                    override fun onSuccess(result: List<AccountsMigrationResource.MigrationAccount>) {
                        // Account migration finished successfully - array of all Futurae user_ids which were migrated successfully
                    }

                    override fun onError(throwable: Throwable) {
                        // Account migration failed. Handle the error
                    }
})

The method executeAccountMigrationWithSDKPin should be called in either of the below scenarios:

  • If the accounts to be recovered were originally enrolled on an SDK configured with SDK PIN (or later switched to an SDK PIN configuration), then the user must be asked to insert the SDK PIN configured on the previous SDK in order to confirm the account migration process, and the inserted SDK PIN is provided as argument to the executeAccountMigrationWithSDKPin method.
  • If the accounts to be recovered were not protected by an SDK PIN and the current lock configuration type is SDK_PIN_WITH_BIOMETRICS_OPTIONAL the provided SDK PIN to the executeAccountMigrationWithSDKPin method will be used as the new SDK PIN for unlocking the SDK.

IMPORTANT: In order for account migration to be feasible on the new device, the following are required:

  • The migration data of the SDK in the previous app installation must have been previously backed up, so that the SDK in the fresh app installation can retrieve them. See Enabling SDK Backup and Restore for instructions on how to achieve this.
  • The SDK in the new app installation must have no other accounts currently or previously enrolled.

You may catch exceptions thrown by the checkAccountMigrationPossible() and executeAccountMigration() methods by catching com.futurae.sdk.AccountsMigrationException or for specific cases the following exceptions that inherit from it:

Enabling SDK Backup and Restore

In order for the Futurae Android SDK to be able to include the necessary data in the Android backup, as well as retrieve them, you need to create a BackupAgent class (extending android.app.backup.BackupAgent) and declare it in your AndroidManifest.xml file as follows:

<application
  android:allowBackup="true"
  android:backupAgent="<PATH_TO_BACKUPAGENT_CLASS>">

In your BackupAgent class make sure to override onBackup(...) and onRestore(...) like in the following example. If you want to backup and restore only Futurae related data, invoke the onBackupAccounts() inside onBackup(...) and onRestoreAccounts() inside onRestore(...). On the other hand, if you have your own data to backup and restore, then during onRestore(...) iterate the keys inside BackupDataInput and pass the entity data to the SDK’s onRestoreAccountsData(...):

@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
  try {
    // App-specific backup logic, if you have any, goes here...

    com.futurae.sdk.BackupAgent.onBackupAccounts(this, oldState, data, newState);
  } catch (IOException e) {
    // Handle exception
  }
}

@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
  try {
    // If your app backs up data that you want to restore in your own code by iterating data keys/values,
    // then all the data your app code doesn't handle must be passed to com.futurae.sdk.BackupAgent.onRestoreAccountsData(Context,BackupDataInput).
    // For a detailed implementation example, check our public demo app.
    //
    // Otherwise let the SDK handle the migration entirely by calling com.futurae.sdk.BackupAgent.onRestoreAccounts().
    com.futurae.sdk.BackupAgent.onRestoreAccounts(this, data, appVersionCode, newState);
  } catch (IOException e) {
    // Handle exception
  }
}

The signatures of the two methods are the following:

public static void onBackupAccounts(Context context, ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException
public static void onRestoreAccounts(Context context, BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException

Adaptive

Using state-of-the-art technology, Adaptive reduces fraud risk by analyzing numerous device-inherit and environmental signals simultaneously to build a user-specific secure context.

Permissions

Before enabling the Adaptive feature, make sure the following permissions are present in your app’s AndroidManifest.xml and requested during runtime:

<!-- Runtime permissions -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission
    android:name="android.permission.BLUETOOTH"
    android:maxSdkVersion="30" />
<uses-permission
    android:name="android.permission.BLUETOOTH_ADMIN"
    android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- For Android 13 or higher -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />

<!-- Static permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

Usage

In order to enable the adaptive mechanism, add the extra adaptive-sdk dependency to your app/builld.gradle file. Refer to the android-adaptive-sdk github repository for the latest release.

repositories {
    maven {
        url "https://artifactory.futurae.com/artifactory/futurae-mobile"
        credentials {
            username = "anonymous"
            password = ""
        }
    }
}

dependencies {
   implementation com.futurae.sdk:adaptive-sdk:x.x.x
}

To enable or disable the Adaptive SDK, call the corresponding method after having initialized the FuturaeSDK:

class App : Application() {

    override fun onCreate() {
        super.onCreate()
        FuturaeSDK.launch(
            this, SDKConfiguration.Builder()
                .setLockConfigurationType(config)
                .setUnlockDuration(unlockDuration)
                .setInvalidatedByBiometricChange(true)
                .setUnlockedDeviceRequired(true)
                .setSkipHardwareSecurity(false)
                .build()
        )
    }

    fun enableAdaptive( ) {
        FuturaeSDK.enableAdaptive(this)
    }


    fun disableAdaptive() {
        FuturaeSDK.disableAdaptive()
    }

    fun isAdaptiveEnabled() : Boolean {
        FuturaeSDK.isAdaptiveEnabled()
    }
}

As long as Adaptive SDK is enabled, context collections will occur regularly. The data collected depends on the runtime permissions which were granted to the app as well as the device’s quick-settings. E.g. location data will not be available if the user’s device has the location setting turned off. Refer to the (Permissions)[#Permissions] section for more.

User Risk Radar

User Risk Radar is an Adaptive solution that provides an effective mechanism to reduce the risk of fraudulent authentication or transaction signing attempts. For further details on how this feature works, refer to the User Risk Radar guide.

To use this feature, follow the instructions described on the sections Permissions and Usage. Additionally, contact support@futurae.com to enable User Risk Radar for your Futurae Service.

Adaptive Account Recovery

Adaptive Account Recovery provides the same functionality as Automatic Account Recovery but with an additional security layer that validates if the user is on a trusted context, before allowing the accounts to be recovered. For more details about this feature refer to the Adaptive Account Recovery guide.

Whenever a user attempts an adaptive account recovery in a new app installation (or reinstallation), the Futurae SDK performs a fresh scan to gather the available contextual data of the new device, and subsequently compares it with the data which has been previously observed for the specific user. If the new device is in a so-called trusted context, then the account migration request is deemed safe and hereby allowed. If the context is not known, the request is denied and the account recovery process is not allowed to happen automatically. Instead the user should be prompted to contact the Customer’s support desk, in order to perform an assisted activation of the new device.

To use this feature, follow the instructions described on the sections Permissions and Usage. Additionally, contact support@futurae.com to enable Adaptive Account Recovery for your Futurae Service.

Adaptive Account Recovery is executed using the same methods checkForMigratableAccounts() and executeAccountMigration() as Automatic Account Recovery.

Errors

If Adaptive is enabled, then calling executeAccountMigration() may also return one of the following Adaptive errors:

Public key pinning

Public key pinning is by default enabled in Futurae Android SDK so that it only accepts a specific list of public keys when connecting to the Futurae backend over TLS. No further configuration is necessary. From version 2.3.0 and onwards, the SDK further restricts allowed TLS ciphers to OkHttp RESTRICTED_TLS.

Reset SDK data

The SDK provides a convenient method to clear all SDK internal data, including accounts, cryptographic keys and secrets, as well as the lock configuration. After successfully calling the reset() function, all the enrolled accounts will be removed, and they will need to be enrolled again if needed.

The SDK may be reset in the following situations:

Integrity Verdict

The SDK can detect the integrity of the App and Device by calling getIntegrityVerdict function. This function returns an object of IntegrityResult which contains a verdict about the device, application and application's channel of acquisition. For example, you can perform the following check to detect a rooted device or emulator:
FuturaeSDK.getClient().getIntegrityVerdict(object : Callback<IntegrityResult> {
    override fun onSuccess(result: IntegrityResult) {
        result.deviceVerdicts.contains(DeviceVerdict.MEETS_NO_INTEGRITY)
    }

    override fun onError(throwable: Throwable) {
        //...
    }
})

SDK protected functions

Some of the SDK functions are protected with an extra security layer, therefore they can only be called by the app when the SDK is in an unlocked status. In order to unlock the SDK, user presence must be verified depending on the SDK lockConfigurationType (see User presence verification section).

SDK protected functions

SDK unlock functions

The SDK provides unlock functions that when called, user presence is verified, and if successful the SDK is unlocked for the configured unlockDuration. The unlock functions which may be used, depend on the configured lockConfigurationType (see User presence verification section).

SDK unlock functions

Troubleshooting

Supporting Android devices means supporting a vast array of devices, often with different combinations of hardware and software which may lead to unpredictable behavior, especially when it comes to variant proprietary implementations of the OS and the Keystore API. To handle issues that can occur for a small subset of users related to cryptography, the Futurae Android SDK provides a set of tools to identify, work-around, and, if possible, remediate such issues.

SDK encrypted storage corruption

The SDK’s encrypted storage may become corrupted either due to tampering with the host app internal storage or keystore corruption. If the SDK encounters such an error, it will throw either LockCorruptedStateException during SDK launch or FTMissingTokensException during any other SDK operation. If one of those two exceptions is raised, you can attempt to recover the SDK by calling FuturaeSDK.launchAccountRecovery. Calling this function will restore the SDK to a normal functioning state as long as the SDK’s signing key is still intact.

Public key synchronization

The SDK is able to self-repair public key synchronization issues. However if SDK session authentications fail with exception FTApiInvalidPublicKeyException it means the SDK and authentication server are out of sync with regards to public key exchange. To remediate this, call FuturaeClient.uploadPublicKey to upload the SDK’s public key to the server.

Validate SDK signing keys

If SDK operations return unexpected errors such as LockUnexpectedException, LockKeyNotFoundException, or FTKeystoreException, the validity of SDK signing keys can be verified by calling FuturaeSDK.validateSDKSigning. This method will throw an exception based on the error discovered:

SDK Debug Utilities

The SDK also provides a set of functions to manually corrupt the SDK so you can test and debug how your app handles these errors, via FuturaeDebugUtils

  • corruptDBTokens - Corrupts the DB by erasing the account’s encrypted tokens stored in the SDK’s database
  • clearEncryptedTokens - Clears all of the SDK’s encrypted tokens
  • corruptV1Keys - Clears the SDK’s v1 legacy cryptographic keys. Use this utility to create the necessary conditions that lead to a failed migration of SDK v1 to v2
  • corruptV2Keys - Clears the SDK’s v2 cryptographic keys
  • corruptEncryptedStorageKey - Clears the SDK’s v2 encrypted storage key