Skip to main content
Skip table of contents

Integration guide for Android SDK

This documentation describes how to integrate your Android app with TrustBuilder MFA.

The TrustBuilder MFA SDK for Android has been released on June 2022 with a new sample application and a new documentation.

The TrustBuilder MFA SDK for Android gives you access to the following features:

  • Activation

  • Synchronize

  • Update PIN code

  • Unlock Device

  • Get online OTP

  • Get offline OTP

  • Check pending push

  • Online seal

  • Offline seal

To implement these features, you will need to call functions from the mAccess library. The application implementing the mAccess library should be linked to a White Label Service.

Useful guides

Requirements

  • Latest version of Android Studio — developer.android.com/sdk - (or other Gradle enabled build system)

  • The current version of the TrustBuilder MFA SDK for Android requires:

    • Minimum SDK : Android Nougat (API 24) or higher

    • Target API: Android 12 (API 31)

  • Java JDK 8 or later

  • Download the TrustBuilder MFA SDK for Android from Resources Downloads. You can download:

    • the mAccess library (Java)

    • the source code of a sample application implementing TrustBuilder MFA features (Java)

  • Request the mAccess ID (MAC ID) from the admin

How does the admin retrieve the MAC ID?

To retrieve the MAC ID, the administrator should log in to the admin console, in a White Label Service (See Creating a White Label Service). In the Service Parameters tab, the admin should scroll down to the mAccess (mobile SDK) section. The MAC ID is displayed and can be provided to the developer.

mAccess alias or macid: 84f7f57b18bee5e8f5da7c3248933796

  • If you use sealing feature, ask the admin to set the ‘Transaction sealing’ option to ‘Yes’

    In the administration console > Service Parameters tab

Security Best Practices

This sub-section reminds some best practices that may have a significant and positive impact on your app's security. These best practices are implemented in the sample application available in the SDK for Android.

Source: Android developers security best practices

Click to read the security best practices

Security in the manifest

We recommend you to implement the following securities in the manifest file.

  1. Set access to your app’s activity

All the activities implemented in the application, except from the “Launcher”, cannot be launched by anything but the application itself. Even the Android system itself is not able to do it.

This can be achieved by adding “exported=false” in the activity tag in the manifest as follow :

JAVA
<activity
 android:name=".MenuActivity"
 android:exported="false" />
 
Instant now = Instant.now();

More information about android:exported

  1. Set the Network Security Configuration

The Network Security Configuration feature lets apps customize their network security settings in a safe, declarative configuration file without modifying app code. These settings can be configured for specific domains and for a specific app.

In the sample application, in our Network Security Configuration, we declared the domain “http://www.myinwebo.com ”, with the option cleartextTrafficPermitted to false to force HTTPS when contacting the TrustBuilder web services. This protects sensitive traffic from hostile networks.

JAVA
<network-security-config>
 <domain-config cleartextTrafficPermitted="false">
 <domain includeSubdomains="false">www.myinwebo.com</domain>
 </domain-config>
</network-security-config>

More information about Network security configuration - Opt out of cleartext traffic

Set data encryption

In the sample application, the encryption is handled by the CryptographyManager. The CryptographyManager is implemented as a Singleton to avoid Thread concurrency when accessing the AndroidKeyStore.

It must be instantiated like this:

JAVA
CryptographyManager cm = CryptographyManager.getInstance();

The algorithm used for encryption is the AES-GCM algorithm (Advanced Encryption Standard - Galois/Counter Mode), a block cipher mode of operation using hash (learn more: https://datatracker.ietf.org/doc/html/rfc7714).

The ciphers used for encryption and decryption come from the core of the JCE (Java Cryptographic Extension) framework. These ciphers are instantiated with the algorithm that we talked about just above, and initialized with a SecretKey. A SecretKey is a symmetric key constructed by the KeyGenerator through the KeyGeneratorSpi. Once generated, this SecretKey is safely stored in the AndroidKeyStore and can be reused indefinitely. The purpose of the Android Keystore is to keep the key material outside of the Android operating system entirely, and in a secure location referred to as the Trusted Execution Environment (TEE). Note that if the Cipher is intended to be used with the Biometric prompt, the SecretKey must be instantiated with parameter mUserAuthenticationRequired set to true.

Here is an example of how to create an encryption cipher:

JAVA
// Retrieving an AES-GCM algorithm Cipher
Cipher cipher = getCipher();

// Retrieving the SecretKey stored in the Android KeyStore
SecretKey secretKey = getOrCreateSecretKey(keyName,
usingUserAuthentication);

// Initializing Cipher in Encrypt Mode
cipher.init(Cipher.ENCRYPT_MODE, secretKey);

Once the Cipher is initialized with this SecretKey and the Encryption or Decryption mode, it can be used for Encryption or Decryption. A Cipher can be used indefinitely but for one purpose only. To decrypt an encrypted data, the initialization vector returned by the Cipher when encrypting must be the same as the one used for decryption. Otherwise, the cipher won’t be able to decrypt whatsoever. In case of an Encryption or Decryption exception, a CryptographyException is raised.

Keep reading to know how cryptography handling is used for Biometrics purposes.

Biometrics: securing the user data

In our sample application, the biometrics are handled by the BiometricService. The BiometricService is instantiated by the static function with, providing the activity where the service is needed:

JAVA
BiometricService biometricService = BiometricService.with(this);

The service is not intended to be shared between the activities as every Biometrics prompt should use its own Biometric service.

The Service uses the BiometricManager, an Android API to access the biometrics features of the user phone. As well as the SecretKey, the biometrics engine safely operates on the TEE by matching the biometric used by the one safely stored in said TEE.

More information about biometric login flows in android application.

The BiometricManager can display a Biometrics Prompt quite easily :

JAVA
// Creating a Biometric Prompt with the current activity
BiometricPrompt biometricPrompt = new BiometricPrompt(
 this.activity,
 ContextCompat.getMainExecutor(this.activity),
 new BiometricPrompt.AuthenticationCallback() {
 ...
 }
);
// Creating a Biometric Prompt Info
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
 ...
 .build();
// Displaying Biometric Prompt with the Cypher
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));

Now that we know how to display a BiometricPrompt, we need to learn how to add cryptography in the process to secure the user data.

As we saw, both the Keystore system and the Biometrics system on Android provide security measures on their own, especially since they keep their materials and sensitive operations in the TEE. But data becomes even more secure when you require biometrics authentication to unlock a SecretKey that’s associated with the application. That’s because the whole transaction takes place in the TEE.

Biometrics and Cryptography can work together to protect user data. First, as we saw on the Set data Encryption section, we will need to create a SecretKey in the Android KeyStore setting the mUserAuthenticationRequired to true. This key newly generated will only be accessible if accessed through Biometrics, with a valid transaction of course.

In order to register Biometrics and retrieve it, the Biometrics works with what we call a Biokey. The Biokey is a randomly generated BigInteger, uniformly distributed over the range 0 to 2^130, converted to a 32 radix String. This Biokey is communicated to TrustBuilder and will be used to generate Biometrics One Time Passwords (see Features implementation section below). This BioKey will be stored encrypted on the Shared Preferences, along with its initialization vector and will be retrieved through Biometrics verification.

Root security

In the sample application, in order to verify that the current device is rooted, we are using multiple methods :

  • Test-Keys checks: Test-Keys has to do with how the kernel is signed when it is compiled. By default, stock Android ROMs from Google are built with release-keys tags. Test-Keys means it is signed with a custom key generated by a third-party developer. This can be accomplished by checking in build properties(“android.os.Build.TAGS”) for test-keys.

    JAVA
    String buildTags = android.os.Build.TAGS;
    return buildTags != null && buildTags.contains("test-keys");
  • “su” command check: Su binary check is to identify the superuser in the device. This binary is installed when trying to root a phone. These files are necessary so that one can root their phone and become a superuser. This can be accomplished by executing commands like ‘/system/xbin/which su’ or ‘su’. If the command can be executed, then the device is rooted.

    JAVA
    return canExecuteCommand("/system/xbin/which su") ||
    canExecuteCommand("su");
  • Looking for binary paths: the paths checked can reveal that the device has been rooted. Only one of those found will trigger the root check.

    JAVA
    String[] paths = {
     "/system/app/Superuser.apk",
     "/sbin/su",
     "/system/bin/su",
     "/system/xbin/su",
     "/data/local/xbin/su",
     "/data/local/bin/su",
     "/system/sd/xbin/su",
     "/system/bin/failsafe/su",
     "/data/local/su",
     "/su/bin/su"
     };
     for (String path : paths) {
     if (new File(path).exists()) {
     return true;
     }
     }
     return false;
     }

We can also check if certain apps are installed on the device.

CODE
private static boolean isAnyDangerousAppInstalled(Context context,
String[] packages) {
    boolean result = false;
    PackageManager pm = context.getPackageManager();
    for (String packageName : packages) {
        try {
            pm.getPackageInfo(packageName, 0);
            result = true;
        } catch (PackageManager.NameNotFoundException e) {
        }
    }
    return result;
}

Then we need to provide a list of apps that should be verified. This list needs to be updated when new dangerous apps appear.

We can sort these app in different categories:

  • Apps used to root the device

  • Apps used to hide the fact that the device is rooted

  • Dangerous App

For example:

CODE
private static final String[] knownRootAppsPackages = {
        "com.noshufou.android.su",
        "com.noshufou.android.su.elite",
        "eu.chainfire.supersu",
        "com.koushikdutta.superuser",
        "com.thirdparty.superuser",
        "com.yellowes.su",
        "com.zachspong.temprootremovejb",
        "com.ramdroid.appquarantine",
        "eu.chainfire.supersu"
};

public static final String[] knownDangerousAppsPackages = {
        "com.koushikdutta.rommanager",
        "com.koushikdutta.rommanager.license",
        "com.dimonvideo.luckypatcher",
        "com.chelpus.lackypatch",
        "com.ramdroid.appquarantine",
        "com.ramdroid.appquarantinepro"
};

public static final String[] knownRootCloakingPackages = {
        "com.devadvance.rootcloak",
        "com.devadvance.rootcloakplus",
        "de.robv.android.xposed.installer",
        "com.saurik.substrate",
        "com.zachspong.temprootremovejb",
        "com.amphoras.hidemyroot",
        "com.amphoras.hidemyrootadfree",
        "com.formyhm.hiderootPremium",
        "com.formyhm.hideroot"
};

Preventing screenshot

In Android, we can manage the application’s default behavior and user interactions handling with “flags”. Those flags are part of the WindowManager.LayoutParams class.

In our sample application, and to prevent screenshots, we are using the flag FLAG_SECURE. This flag treats the content of the window as secure, preventing it from appearing in screenshots or from being viewed on non-secure displays. This flag is set in the most common activity as follow:

CODE
super.getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);

More information about FLAG_SECURE.

In memory pin

It is important to reduce the number of occurrences of your pin code in memory as much as possible.

The best way to do this is to avoid storing it completely in String Objects. Since strings are always transmitted as a copy in Java, it is very difficult to clean the pin after use. Good alternatives are char[] or byte[].

To prevent your Android UI Component EditText from using Strings, you can override the way CharSequences are used by creating your own implementation of Editable.

CODE
text.setEditableFactory(new Editable.Factory() {  
  public Editable newEditable(CharSequence source) {
    PinEditable pinEditable = new PinEditable();
    pinEditable.append(source);
    return pinEditable;
  }
});
CODE
public class PinEditable extends SpannableStringBuilder {
  public char[] getPin() {
    char[] pin = new char[length()];
    getChars(0, length(), pin, 0);
    clear();
    return pin;
  }
  @Override
  public String toString() {
    return "";
  }
  @Override
  public int hashCode() {
    return 0;
  }
}

It it also important to clean pin data as soon as possible when leaving a scope.

CODE
private static byte[] toBytes(char[] chars) {
  CharBuffer charBuffer = CharBuffer.wrap(chars);
  ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode(charBuffer);
  byte[] bytes = Arrays.copyOfRange(byteBuffer.array(),
           byteBuffer.position(), byteBuffer.limit());
  Arrays.fill(byteBuffer.array(), (byte) 0); // clear sensitive data
  return bytes;
}
public static byte[] getBytesFromTextview(EditText textView) {
  Editable seq = textView.getText();
  char[] textValue = null;
  if (seq instanceof PinEditable) {
      textValue = ((PinEditable) seq).getPin();
  } else {
      textValue = new char[seq.length()];
      seq.getChars(0, seq.length(), textValue, 0);
  }
  textView.setText("");
  seq.clear();
  seq.clearSpans();
  byte[] bytes = toBytes(textValue);
  Arrays.fill(textValue, (char) 0);
  return bytes;
}

Protecting the One-Time Password

To prevent insecure use of the one-time password (OTP), it is recommended that you do not allow the generated OTP to be copied to the device's clipboard.

General Principles

Local storage

The internal state of the application must be locally stored, and updated each time the library is accessed. This internal state is an ASCII string provides by the function IW.StorageDataGet ().

The implementation of this storage is system-dependent, and up to the developer to design.

After each call to a library function, the host must call IW.StorageDataChanged (), in order to find out if the internal state has changed. If this function returns a non-zero integer, the host must call IW.StorageDataGet () and then update the local storage. When the host application starts, it must get the ASCII string stored locally and give it to the library using the function IW.StorageDataSet ().

Synchronous or Asynchronous mode

Some mAccess library functions execute network calls, namely webservice calls, to query TrustBuilder servers. And most mAccess webservice calls are divided in two steps, i.e. two functions, a start function and a finalize function.

There are two different ways of implementing the webservice calls: synchronously or asynchronously. The code architecture of the host will vary according to the chosen mode.

  • Synchronous mode → The host function myAction will execute the IWActionStart () function and will directly fetch the result of the webservice call. If the result of the IWActionStart() is successful it will then execute the IWActionFinalize ().

  • Asynchronous mode → The result of the webservice calls will be handled by callback functions which are passed to the start and finalize functions.

Web services calls

mAccess uses platform dependent functions to call TrustBuilder web services. These functions should be part of the host. Code samples expose such functions. You may use them as is or enhance them.

  • Synchronous mode

In synchronous mode, the host code should contain only one function:

CODE
    public String webServiceCall(String url, long timeout);

This function performs a GET request to a specific URL. The call is synchronous, and the timeout is given in milliseconds. The response is directly fetched inside the function. It consists of an XML document (as an ASCII string). In Java, WebServiceCall should return result (XML response) or null if an error occurred.

  • Asynchronous mode

In this case the host code should contain two functions. The function executing the API webservice call:

CODE
  public String webServiceCall(String url, long timeout);

This function performs a GET request to a specific URL. The network call response is handled by a second function:

CODE
HandleWebServiceCallResult: (object result) -> Void

Depending on the platform implementing the mAccess library the way of declaring this handler function and the structure of the fetched result object may vary.

The final API call result (which is normally a property or a field of the result object) is an XML Document (as an ASCII string).

On success, the handler function will have to execute the callback function (passed in argument of the mAccess API asynchronous function) with argument 0 (0 = success). Typically:

CODE
IWSetWsBuffer (string response);
WSCallBack (0);

On failure, the handler function will have to execute the callback function (passed in argument of the mAccess API asynchronous function) with argument 1 (1 = error).

CODE
WSCallBack (1);

Should I use online or offline OTP?

In order to generate an online OTP, mAccess will perform one or more webservice calls to TrustBuilder servers. This mode is suitable for a connected application (online banking for instance), as the token will always be synchronized with TrustBuilder Servers.For a non-connected application (VPN dialer, authenticator-type app), Offline OTP is recommended. In this case, an OTP will be generated without any network call. The drawback of this method is the possibility for the token to desynchronize.

Configure the build.gradle app file

Android Studio projects contain a top-level build file and a build file for each module. The build files are called build.gradle.

In your app individual module build.gradle file, edit the following elements:

  • defaultConfig → it configures core settings and entries in the manifest file (AndroidManifest.xml) dynamically from the build system. The values in defaultConfig override those in the manifest file. Here is how defaultConfig looks like in our sample application:

    CODE
    defaultConfig {
            applicationId "com.inwebo.demo_android"
            minSdkVersion 24
            targetSdkVersion 31
            versionCode 1
            versionName "1.0"
            buildConfigField "String", "MACID", "\"f9i1f283a8af89bec62731ccdaec758d\""
            buildConfigField "String", "SERVER", "\"https://www.myinwebo.com\""
        }
    • applicationId → The application ID

    • minSdkVersion → Sets minimum SDK version The minimum version of the Android platform on which the app will run, specified by the platform's API level identifier.

    • targetSdkVersion → Sets the target SDK version to the given value. Specifies the API level on which the app is designed to run.

    • versionCode → version code - a positive integer used as an internal version number.

    • versionName → version name - a string used as the version number shown to users.

    • buildConfigField → Adds a new field to the generated BuildConfig class. The field is generated as: <type> <name> = <value>; In our case, as the type is a String, then the value should include quotes.

      • Add a MACID string and past the mAccess ID as a value (see Requirements to know how to get the MAC ID)

      • Add a SERVER string and enter the server URL as a value. Most often, the value will be set to https://www.myinwebo.com /

  • dependencies → declares the dependencies for this application module. You should specify the mAccess library .jar file, with the right version.

    CODE
    implementation files('libs/iwlib-mac-<version>.jar')

Push notifications

Push notifications can notify the user of an authentication request. Before configuring push notifications, we recommend that you learn about the push notifications mechanism for the platform.

Considerations for mAccess SDK:

  • Unique Identifier of User or Device: allows TrustBuilder to send push notifications to your app. It must be sent to TrustBuilder servers using the IW.PushRegistrationStart() function.

  • Variables received inside the notifications: these variables (such as the activation code, transaction alias) will be provided as arguments in the mAccess API push activation and authentication functions.

  • Notification Platform used on the Application: must be set using the IW.SetDeviceOS() function. The platform must match the mAccess push notification parameters configured in the Admin Console. For Android, only Firebase is still operational SetDeviceOS("firebase").

Deprecated versions

  • Firebase Cloud Messaging (FCM) Legacy API was officially deprecated on June 2024 (More information). If your configuration is still based on FCM Legacy API, you should migrate to API V1. Please refer to this detailed note: How to upgrade to API V1.

  • Google Cloud Messaging (GCM) was officially deprecated by Google on April 2018. If your configuration is still based on GCM, you should migrate to Firebase Cloud Messaging.

Firebase Messaging

Firebase Messaging is a solution used to send remote push notifications across Android, iOS and web devices. It allows both background and in-app notifications. Your project should be created here: https://console.firebase.google.com

Background notifications

1- Add your android application in your project.

  • Go into Settings > General Settings > Add a new application

  • Enter the package name of your application. If your app uses many different package names you should create one for each.

  • Download the Google services JSON file and place it in your app module.

2- Add the Firebase dependencies in your build.gradle file

  • Check that google() is listed in your registered dependencies repositories on the top level build.gradle file.

  • In your app build.gradle file, you should add:

3- Handle tap on the notification

  • Notification payload can be retrieved from your intent extra from your launching activity. Either from onCreate() if the app was closed or from onNewIntent() if the app was in the background.

In App notifications

You should add a service that will extend FirebaseMessagingService. It will be the entry point for every notification received while the app is in foreground.

CODE
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private final String TAG = IWFirebaseMessagingService.class.getSimpleName();
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// Not getting messages here? See why this may be: https://goo.gl/39bRNJ
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " +
remoteMessage.getNotification().getBody());
}
handleMessage(remoteMessage);
}
[...]
}
Notification content

Notifications sent to Firebase by our backend have the following format:

API V1

LEGACY API

CODE
{
    "message":{
          "data":{
              "deviceAlias":"9b2a9506e2cfeeb2af2666a80f0c666d",
              "action":"authenticate",
              "alias":"d1aeb3e814866b2de4709080061ba666",
              "serviceid":"99",
              "serviceName":"MyServiceName"
          },
          "android":{
              "collapse_key":"<collapse_key>",
              "restricted_package_name":"<package_name>",
              "priority":"high",
              "ttl":"70s"
          },
          "token":"<firebase_token>"
          }
}
CODE
{
    "collapse_key":"<collapse_key>",
    "restricted_package_name":"<package_name>",
    "data":{
        "deviceAlias":"9b2a9506e2cfeeb2af2666a80f0c666d",
        "action":"authenticate",
        "alias":"d1aeb3e814866b2de4709080061ba666",
        "serviceid":"99",
        "serviceName":"MyServiceName"
    },
    "time_to_live":70,
    "to":"<firebase_token>",
    "priority":"high"
}

Firebase parameters in the Admin Console

From the admin console, in a White Label Service (See Creating a White Label Service):

  1. Go to Service Parameters tab.

  2. Scroll down to the mAccess push notification parameters section.

  3. Click on + for Firebase parameters.

    Firebase parameters.png

    (error) The configuration under “Android parameters” should not be used. This was the Google Cloud Messaging (GCM) configuration, which is now obsolete.

  4. In Firebase parameters, set the notification parameters:

    • Choose V1 API.

      image-20240905-101749.png

      ⚠️ Firebase Cloud Messaging (FCM) Legacy API was officially deprecated on June 2024 (More information). If your configuration is still based on FCM Legacy API, you should migrate to API V1. Please refer to this detailed note: How to upgrade to API V1.

    • Paste the content of the service account JSON file.
      It the JSON file containing the private key for your project. You can generate it in the Firebase console.

    • Enter the Notification Collapse Key (Optional).
      It defines the key used to send collapsible messages to your application. When several notifications are sent in a short period of time, it allows these notifications to be aggregated.

  5. Click on Add.

Two applications on the same service

To have two applications on the same service, which receive push notifications, you should:

  1. Make sure the the SetDeviceOS function is set to "firebase".

  2. Set notifications in the Admin Console (Service parameters tab > "mAccess push notification parameters" > Firebase parameters): leave “Notification Collapse Key” empty.

Initialization

To initialize the library, the calls are the following.

  • IW.Init (String SN, String appData)

Your application may provide 2 strings: one should be linked to the device (Serial Number) and the other one to the installation (timestamp of an install directory). These strings should not change over the lifetime of your application. If they do, the application will be locked.

  • IW.HostVersionSet (string version)

The host/application provides its version number. Example: myApp-1.3.0

  • IW.WsServerSet ()

The host/application defines the server value for the web service calls, such as “https://www.myinWebo.com:443”.Returns always true.

  • IW.WsTimeoutSet ()

The host/application defines the timeout value for the web service calls, in millisecond. Returns always true.

  • IW.MaccessSet () provides the mAccess ID, that can be found in the Admin Console

  • Read the ASCII string stored locally, and pass it to the function IW.StorageDataSet ().

  • Determine whether mAccess is activated or not by calling IW.IsActivated (). If this function returns “1”, mAccess is activated.

  • If mAccess is not activated, go Activation Implementation).

  • Determine whether mAccess is blocked or not by calling IW.IsBlocked ().

    • If this function returns “1”, mAccess is blocked. Go to section “Reset” Implementation.

    • If mAccess is activated and not blocked, startup procedure is over and completed successfully

Interaction

In our sample application, the InweboSession is common to every activity, as it is created in a common activity extended by all the others. With this implementation, each activity can call the ‘getApi()’ method that gives access to the TrustBuilder methods.

The TrustBuilder web services

Each web service implemented in the TrustBuilder library is synchronous, we can’t call them directly from the activities because we can’t execute them on the UI thread to avoid blocking UI.
In order to perform web services calls in an asynchronous way, in the sample application, we use the Java Future interface, through promises. The Promise pattern implemented is based on the Java concurrent interface Future, and represented by the class PromiseSupport. The PromiseSupport only handles the 3 states RUNNING, FAILED and COMPLETED, implementing methods to update those states accordingly to the task status.
Then, the Promise class, that extends the PromiseSupport class, implements all the methods to handle every possible action callback during an asynchronous task in a very readable way.

Finally, the InweboPromise is just an upper layer to those promises to handle asynchronous tasks brought by the TrustBuilder library. We can notice that the callbacks are executed on the UI thread so that we can interact with the UI in all the steps of a web service call. The asynchronous method to watch must be declared in the “fulfillInAsync” method of the Promise.

To declare a synchronous webservice call with these methods we just have to write:

CODE
InweboPromise.with(this)
                .fulfillInAsync(() -> inweboService.getApi().Start())
                .thenAccept(responseId -> {
                // action on success
                }).onError(throwable -> {
                // action on error
                });

Error handling

The web services contained in the TrustBuilder library never raises errors, as these errors are directly handled in the library, and an error code is often returned in those web services, error codes handled in the promises callbacks. If the error code returned is not ‘0’, then an explicit dialog will be displayed to the user so that he can see what happened.
In the case of an unsuccessful network call (unreachable network, etc.), the promise callback that will be executed is the “onError”, with the associated throwable that we will log.
An explicit dialog will also be displayed to the user so that he can see what happened, or what is wrong with his request.

Features implementation

This section describes the features implemented in the sample application available in the TrustBuilder MFA java SDK.

Activation

1- Activation code

At the first application launch, the user will face the Activation page, asking for an activation code. When the activation code is entered, the user can click on the button ‘Validate’, that will disable the activation code input, and trigger the code validation:

  • If the code entered is empty, then a dialog will appear to notify the user that code entered is empty. The activation code input will be re-enabled.

  • If the code is not empty, the user can proceed on the activation.

At this point, we only have an activation code. We will now start the activation process on TrustBuilder calling IW.ActivationStart(activationCode)

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activation process will restart.

  • If the webservice does return the response code 0, the user can proceed on the activation

2- PIN Code

Now that the activation code has been authorized by TrustBuilder, we have to handle the pin code input. To achieve that, we first ask for the IW.PinMode() to find out if the user has to define a new pin code, or enter its existing pin code for verification. This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, then no PIN code are necessary and we can finalize the process with no pin code. We can call IW.ActivationFinalize() with no PIN value. See section ‘3- Finalize process below’.

  • If the PIN mode is ‘CURRENT’, then the current user PIN is required. Only the PIN input appears

  • If the PIN mode is ‘NEW’, then a new PIN must be defined. The PIN input as well as the PIN confirmation input are displayed

After entering the PIN code, the user can click on the button ‘Validate’, that will disable the PIN code input(s) :

  • If the PIN code entered is empty, or one of the PIN codes in NEW pin mode, then a dialog will appear to notify the user that the code(s) entered is (are) empty. The PIN code input will be re-enabled.

  • In NEW PIN mode, if both codes are not equals, then a dialog will appear to notify the user that the PIN codes entered are not the same. The PIN code input will be re-enabled.

  • In all modes, if PIN is not valid (meaning it’s not 4 numbers long or longer than 6 numbers) then a dialog will appear to notify the user that the PIN code entered is not valid.

  • If the PIN inputs is (are) good, then we can now finalize the process.

We can now finalize the activation process.

3- Finalize process

At this point, we have an Activation code, a valid PIN (or no PIN at all in the case of NONE PIN mode).

We will now start the activation process on TrustBuilder calling IW.ActivationFinalize(activationCode, pinValue):

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activation process will restart.

  • If the webservice does return the response code 0, the TrustBuilder data will be stored on internal storage, a message will be displayed confirming the activation, and the services available will be displayed.

The device is now registered for a user account. The user can use the application with this device to authenticate.

Unlock

Now that the user is authenticated, the device can be locked for several reasons: wrong pin entered a couple times, manual action on the admin console, etc.

When starting any activity, the current user session is checked to know if the device is locked. If that’s the case, the user will arrive on the Unlock page

1- Unlock code

The Unlock page asks for an unlock code. When the unlock code is entered, the user can click on the button ‘Validate’, that will disable the unlock code input, and trigger the code validation:

  • If the code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The unlock code input will be re-enabled.

  • If the code is not empty, the user can proceed on the device unlock

At this point, we only have an unlock code. We will now start the unlock process on TrustBuilder calling IW.ResetStart(Code) :

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the unlock process will restart.

  • If the webservice does return the response code 0, the user can proceed on the unlock process

2- PIN Code

Now that the unlock code has been accepted by TrustBuilder, we have to handle the pin code input. To achieve that, we first ask for the IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, then no PIN code are necessary and we can finalize the process with no pin code. We can call IW.ResetFinalize() with no PIN value. See section ‘3- Finalize process below.

  • If the PIN mode is ‘CURRENT’, then the current user PIN is required. Only the PIN input appears

  • If the PIN mode is ‘NEW’, then a new PIN must be defined. The PIN input as well as the PIN confirmation input are displayed

After entering the PIN code, the user can click on the button ‘Validate’, that will disable the PIN code input(s):

  • If the PIN code entered is empty, or one of the PIN codes in NEW pin mode, then a dialog will appear to notify the user that the code(s) entered is (are) empty. The PIN code input will be re-enabled.

  • In NEW PIN mode, if both codes are not equals, then a dialog will appear to notify the user that the PIN codes entered are not the same. The PIN code input will be re-enabled.

  • In all modes, if PIN is not valid, meaning it’s not 4 numbers long or longer than 6 numbers, then a dialog will appear to notify the user that the PIN code entered is not valid.

  • If the PIN inputs is (are) good, then we can now finalize the process

We can now finalize the process.

3- Finalize process

At this point, we have an Unlock code, a valid PIN, or no PIN at all in the case of NONE PIN mode.

We will now start the unlock process on TrustBuilder calling IW.ResetFinalize(unlockCode, pinValue):

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the unlock process will restart.

  • If the webservice does return the response code 0, the authentication data will be stored on internal storage, a message will be displayed confirming the unlocking, and the services available will be displayed.

Synchronization

In order to synchronize information between the application and TrustBuilder server, you can perform a Synchronization.

1- Starting process

In order to synchronize the application, we first need to call the webservice IW.SynchronizeStart():

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activity will close.

  • If the webservice does return the response code 0, the user can proceed on the synchronize process, and the PIN input appears on the screen.

2- Finalizing process

Now that the process has been started by TrustBuilder, we have to handle the pin code input. To achieve that, we first ask for the IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, then no PIN code are necessary and we can finalize the process with no pin code. The method IW.ResetFinalize() is called with no PIN value.

  • If the PIN mode is ‘CURRENT’, then the current user PIN is required.

After entering the PIN code, the user can click on the button ‘Synchronize’, that will disable the PIN code input :

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

The method IW.SynchronizeFinalize(pinCode) is now called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the Synchronize process is restarted.

  • If the webservice does return the response code 0, a message is displayed confirming that the application has been Synchronized. Then the user is redirected to the main menu.

PIN code update

Now that a PIN is set for a user, it has the possibility to update it directly on the application.

1- Starting process

In order to update the PIN, we first need to call the webservice IW.PwdUpdateStart():

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activity will close.

  • If the webservice does return the response code 0, the user can proceed on the pin code update process, and the PIN inputs will appear on the screen.

2- Finalizing process

Now that the process has been started by TrustBuilder, we have to handle the pin code input:

The user will have to enter the current PIN, the new PIN and its confirmation. When this has been done, the user can click on the button ‘Update Pin Code’, that will disabled inputs and trigger pin confirmation:

  • If one of the three inputs is empty, then a dialog will appear to notify the user that the code(s) entered is (are) empty. The PIN code inputs will be re-enabled

  • If the new pin is not equal to its confirmation value, then a dialog will appear to notify the user that the confirmation code is not good. The PIN code inputs will be re-enabled.

  • If PIN is not valid, meaning it’s not 4 numbers long or longer than 6 numbers, then a dialog will appear to notify the user that the PIN code entered is not valid. The PIN code inputs will be re-enabled.

  • Else, the process can be finalized

To finalize the process, the webservice IW.PwdUpdateFinalize(newPinCode, currentPinCode) will be called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the process will restart.

  • If the webservice does return the response code 0, a message is displayed confirming that the PIN Code has been updated. Then the user is redirected to the main menu.

3- Reset all registered biokeys(Optional)

After a PIN code update, for security reasons, you may want to remove all the registered biometrics from user’s devices. We will first call IW.UnsetBiokeysStart() to start the reset process. To finalize the process IW.UnsetBiokeysFinalize(pinCode) will be called.

Biometrics registration

When a user is starting this activity, a first check is done to ensure that the current device is supporting Biometrics. This shouldn’t be an issue as the Biometrics registration menu item is not displayed in the main menu if Biometrics are not available.

Then, we will check if a biokey is already registered in the current device. If that’s the case, the message ‘You already have biometrics registered, but you can override it.’ will be displayed, but the user can still access the process.

1- Starting process

In order to register a new Biometric, we first need to call the webservice IW.SetBiokeyStart():

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activity will close.

  • If the webservice does return the response code 0, the user can proceed on the biometric registration process, and the PIN inputs will appear on the screen.

2- Finalizing process

Now that the process has been started by TrustBuilder, we have to handle the pin code input. To achieve that, we first ask for the IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, then no PIN code are necessary and we can finalize the process with no pin code

  • If the PIN mode is ‘CURRENT’, then the current user PIN is required.

After entering the PIN code, the user can click on the button ‘Register’, that will disable the PIN code input :

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

To finalize the process, the user clicks on the button ‘Register’. A Biometric Prompt will be displayed:

  • If the Biometric authentication is successful, then the process can continue

  • If the Biometric authentication is not successful, the process is restarted to step 1

To finalize the process, the method IW.SetBiokeyFinalize(pinCode) is now called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the Biometric registration process is restarted to step 1.

  • If the webservice does return the response code 0, a message is displayed confirming that the Biometric has been registered. Then the user is redirected to the main menu.

Biometric keys can be all reset when updating PIN code - See “PIN code update” above

Online OTP Generation

The online One Time Password (OTP) generation process can be done in two ways : either with the current PIN or with a registered Biometric.

1- Starting process

In order to start an Online OTP generation, we first need to call the webservice IW.OnlineOtpStart(0):

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activity will close.

  • If the webservice does return the response code 0, the user can proceed on the OTP generation

2- Finalizing process

Generation with PIN

In the case of a generation with the PIN code, we first must retrieve the PIN mode with IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, the user does not need to enter the current PIN, and the generation can be done without PIN

  • If the PIN mode contains the ‘CURRENT’ code bit (1 -> 0001), then the PIN input appears and the user has to enter his PIN

After entering the PIN code, the user can click on the button ‘Generate’, that will disable the PIN code input :

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

To finalize the process, the method IW.OnlineOtpFinalize(0, pinCode) is now called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the OTP generation process is restarted to step 1.

  • If the webservice does return the response code 0, the generated OTP is retrieved through the method IW.OtpAnswerOtp(), is displayed on the user screen.

Generation using Biometrics

The Biometrics OTP generation is using the BiometricService described in section Biometrics, in Security Guidelines.

When a user is starting the process, a first check is done to ensure that a Biometric is already registered. If that is not the case, a Dialog is displayed with the message : ‘No Biometrics Available’, and the user is redirected to the main menu.

To start the finalization process, the user has to click on the ‘Generate’ button.

In the case of a generation with the Biometrics, we first must retrieve the PIN mode with IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, the user does not need to enter his Biometrics, and the generation can be done without it

  • If the PIN mode is ‘CURRENT’, this indicates that the biometric option may not be activated in the user account settings, or that no biometrics are available in the application. Either case, the activity is exited with a Dialog : ‘No biometrics registered or the biometric option is not authorized in your account settings’

  • If the PIN mode contains the ‘BIO’ code bit (8 -> 1000), then the Biometric Prompt is displayed and the user has to authenticate

If needed, the authentication can led to three situations:

  • The authentication went well, and the BioKey has been retrieved. The process can be finalized with the BioKey

  • The authentication went well, but the BioKey has not been retrieved due to a CryptographyException. If that’s the case, it means that either the IV or the encrypted data has been corrupted. In this case, the Shared Preferences entries will be deleted and the user will have to register a new Biometric.

  • The use user failed to authenticate, and the process is restarted to step 1

To finalize the process, the webservice IW.OnlineOtpFinalizeExt(0, bioKey, 1) is called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the OTP generation process is restarted to step 1.

  • If the webservice does return the response code 0, the generated OTP is retrieved through the method IW.OtpAnswerOtp(), is displayed on the user screen.

Offline OTP Generation

In the case of an offline OTP generation, the process is a little bit different than the online one, even if the UI is nearly the same.

1- Starting process

The offline generation must be done with a perfectly synchronized application. So the first step in the process is to call method IW.otpShouldSynchronize(), to ensure that the application is up to date:

  • If that is not the case, a Dialog is displayed to warn the user that his application is not synchronized, asking him to synchronize it with the Synchronize feature

  • If the application is synchronized, then we can start the Offline OTP generation process

Then we need to know if the PIN is required. To achieve that, we call the method IW.OtpModeQuery(0):

  • If ‘1’ is returned, it means that the PIN is required, and the PIN input is displayed

  • If ‘0’ is returned, the PIN is not required and we do not need the input

2- Finalizing process

If the PIN is required, the user enters it and clicks on the button ‘Generate’:

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

If the PIN is not required, the user only has to click on the button ‘Generate’.

This action will trigger the method IW.OtpGenerate(), that will return a generated OTP. To acknowledge the expiration time of this OTP, we must call IW.DisplayTime() that will return the remaining time in seconds, time that will be displayed on the UI.

Check Pending Push

The pending Push check retrieves a potential pending push notification and displays its information to the user.

1- Setting the Device OS

This can be achieve by calling the method IW.SetDeviceOS(deviceOS).

To use Firebase from Google for notification, you should configure Firebase and call IW.SetDeviceOS("firebase").

2- Starting push registration

Now that the OS is configured, we can start the process by calling the method IW.PushRegistrationStart() :

  • If the response code is ‘0’, register process is ok, we can call method IW.PushRegistrationFinalize()

  • If the response code is other than ‘0’,  the registration process cannot be started and the application displays a dialog with the corresponding error message. The user is then redirected to the main menu

3- Finalizing push registration

If push registration has been successful, we can call the method IW.PushRegistrationFinalize(). This method needs a parameter “pushId” which is the id of the device used for notification push service :

  • If the response code is ‘0’, device is registered and process end well

  • If the response code is other than ‘0’,  the registration process cannot be finalized and the application displays a dialog with the corresponding error message. The user is then redirected to the main menu

4- Starting check pending push process

If the registration process went successful, we are now able to use the check pending push method, to get push details if a push is sent to call the method, users have to click on the “Check pending push” button.

This action will trigger the method IW.CheckPush() :

  • If response code is ‘999’, the application displays a Dialog : ‘No pending push available or mobile push service may not be activated’

  • If response code is ‘0’, a push is available and the application displays the following details under the button : 

    • IW.PushAlias()’ returns the push ID

    • IW.PushAction()’ returns the push action type of the notification (authenticate or activate)

    • IW.PushContext()’ returns notification context if there is one.

  • If response code is other than ‘0’ or ‘999’, the application displays a dialog with the corresponding error message

5- Push Authentication

If the device is activated, a push notification will be sent to request an authentication from the user. It can either be fetched with the method IW.CheckPush() or by implementing Firebase Messaging.

Online seal

The online seal generation process can be done in two ways: either with the current PIN or with a registered Biometric.

Before using sealing feature, be sure that the “Transaction sealing” option is set to “Yes” (See Requirements)

1- Starting process

In order to start a seal generation, we first need to call the webservice IW.OnlineSealStart(0):

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message. Then the activity will close.

  • If the webservice does return the response code 0, the user can proceed on the seal generation

2- Finalizing process

Seal generation with PIN

In the case of a generation with the PIN code, we first must retrieve the PIN mode with IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, the user does not need to enter the current PIN, and the generation can be done without PIN

  • If the PIN mode contains the ‘CURRENT’ code bit (1 -> 0001), then the PIN input appears and the user has to enter his PIN

After entering the PIN code, the user can click on the button ‘Generate’, that will disable the PIN code input :

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

  • A data field is here to represent the data that is going to be sealed

To finalize the process, the method IW.OnlineSealFinalize(0, pinCode, data) is now called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the seal generation process is restarted to step 1.

  • If the webservice does return the response code 0, the generated seal is retrieved through the method IW.SealAnswerOtp(), is displayed on the user screen, along with a button ‘Copy’ to set the generated seal in the device clipboard.

Seal generation using Biometrics

When a user is starting the process, a first check is done to ensure that a Biometric is already registered. If that is not the case, a Dialog is displayed with the message : ‘No Biometrics Available’ and the user is redirected to the main menu.

To start the finalization process, the user has to click on the ‘Generate’ button.

In the case of a generation with the Biometrics, we first must retrieve the PIN mode with IW.PinMode(). This method will return the pin mode that should be used for the current process:

  • If the PIN mode is ‘NONE’, the user does not need to enter his Biometrics, and the generation can be done without it

  • If the PIN mode is ‘CURRENT’, this indicates that the biometric option may not be activated in the user account settings, or that no biometrics are available in the application. Either case, the activity is exited with a Dialog : ‘No biometrics registered or the biometric option is not authorized in your account settings’

  • If the PIN mode contains the ‘BIO’ code bit (8 -> 1000), then the Biometric Prompt is displayed and the user has to authenticate

If needed, the biometric authentication can led to three situations:

  • The biometric authentication went well, and the BioKey has been retrieved. The process can be finalized with the BioKey

  • The biometric authentication went well, but the BioKey has not been retrieved due to a CryptographyException. If that’s the case, it means that either the Initialization Vector or the encrypted data has been corrupted. In this case, the Shared Preferences entries will be deleted and the user will have to register a new Biometric.

  • The user failed to authenticate, and the process is restarted to step 1

To finalize the process, the webservice IW.OnlineSealFinalizeExt(0, bioKey, 1, data) is called:

  • If the webservice does not return a response code 0, the application displays a dialog with the corresponding error message, and the seal generation process is restarted to step 1.

  • If the webservice does return the response code 0, the generated seal is retrieved through the method IW.SealAnswerOtp(), is displayed on the user screen, along with a button ‘Copy’ to set the generated seal in the device clipboard.

Offline seal

In the case of an offline seal, the process is a little bit different than the online one, even if the UI is nearly the same.

Before using sealing feature, be sure that the “Transaction sealing” option is set to “Yes” (See Requirements)

1- Starting process

The offline seal generation must be done with a perfectly synchronized application. So the first step in the process is to call method IW.SealShouldSynchronize(), to ensure that the application is up to date:

  • If that is not the case, a Dialog is displayed to warn the user that his application is not synchronized, asking him to synchronize it with the Synchronize feature

  • If the application is synchronized, then we can start the Offline seal generation process

First we need to know if the PIN is required. To achieve that, we call the method IW.SealModeQuery(0):

  • If ‘1’ is returned, it means that the PIN is required, and the PIN input is displayed

  • If ‘0’ is returned, the PIN is not required and we do not need the input

2- Finalizing process

If the PIN is required, the enters it and clicks on the button ‘Generate’:

  • If the PIN code entered is empty, then a dialog will appear to notify the user that the code entered is empty. The PIN code input will be re-enabled.

  • If the PIN inputs is good, then we can now finalize the process

  • A data field is here to represent the data that is going to be sealed

If the PIN is not required, the user only has to click on the button ‘Generate’.

This action will trigger the method IW.SealGenerate(), that will return a generated seal. To acknowledge the expiration time of this seal, we must call IW.DisplayTime() that will return the remaining time in seconds, time that will be displayed on the UI.

When time comes to expire, then the button ‘Copy’ that copies the seal on the device clipboard will be disabled.

Back to top

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.