Face authentication with Android SDK

You can use face biometrics to reliably verify your customers' identity by biometrically matching a selfie to a known photo of the user. For example, face authentication can act as a step-up safeguard when a logged-in user wants to perform a money transfer or change their profile data. This guide describes how to quickly integrate face authentication into your Android application using our Android SDK, including both the client-side and backend integration.

How it works

Below is an example of a basic flow. Mosaic APIs are shown in pink, along with the relevant integration step.

After initializing the SDK (Step 3), your app starts a verification flow by creating a session in the backend that establishes a secure context and provides a reference image (Step 7 and Step 8) and then starting the session (Step 9). The SDK executes the verification process with the user using the Mosaic experience. Once the user's selfie is submitted, Mosaic starts processing the verification while the SDK polls for its status. Once processing is completed, the SDK notifies the app (Step 4) so it can obtain the verification result (Step 10) and proceed accordingly (Step 11).

Requirements

  • Android 5+ (API level 21+)

Step 1: Configure your app

Admin Portal

To integrate with Mosaic, you'll need to configure an application. From the Applications page, create a new application or use an existing one. From the application settings:

  • For Client type , select native .
  • For Redirect URI , enter your website URL. This is a mandatory field, but it isn't used for this flow.
  • Obtain your client ID and secret, which are autogenerated upon app creation.

Step 2: Add SDK to project

client

Add the following lines in the shared build.gradle file ("allprojects" scope):

Copy
Copied
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {

        maven {
            url('https://transmit.jfrog.io/artifactory/transmit-security-gradle-release-local/')
        }
        mavenCentral()
        google()
    }
}

Add the following in the module build.gradle file (project scope):

Copy
Copied
dependencies {
    implementation("com.ts.sdk:identityverification:1.2.+")
}

Step 3: Initialize the SDK

client
Initialize using strings.xml configuration (recommended)

To do this, update the strings.xml file in your Application with the following content. The CLIENT_ID should be replaced with your client ID from step 1(Step 1).

Copy
Copied
<resources>
    <!-- Mosaic Credentials -->
    <string name="transmit_security_client_id">"CLIENT_ID"</string>
    <string name="transmit_security_base_url">https://api.transmitsecurity.io/</string>
</resources>
Note

The SDK can be configured to work with a different cluster by setting transmit_security_base_url to https://api.eu.transmitsecurity.io/ (for EU) or https://api.ca.transmitsecurity.io/ (for Canada).

Add the code below to your Application Class

KotlinJava
Copy
Copied
class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        TSIdentityVerification.initializeSDK(this)
    }
}
Copy
Copied
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        TSIdentityVerification.initializeSDK(this)

    }
}
Initialize using SDK parameters

Configure the SDK using one of the snippets below, where CLIENT_ID is your client ID (obtained in Step 1)

KotlinJava
Copy
Copied
class MyApplication: Application() {
    override fun onCreate() {
        super.onCreate()
        TSIdentityVerification.initialize(this, "CLIENT_ID", "https://api.transmitsecurity.io/")
    }
}
Copy
Copied
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        TSIdentityVerification.initialize(this, "CLIENT_ID", "https://api.transmitsecurity.io/")

    }
}
Note

The SDK can be configured to work with a different cluster by setting a third initialization parameter to baseUrl : 'https://api.eu.transmitsecurity.io/' (for EU) or baseUrl : 'https://api.ca.transmitsecurity.io/' (for Canada).

Step 4: Observe status updates

client

Once a face authentication process has been initiated (as described in Step 9), the process moves through different statuses. For example, the status will indicate if the process was completed successfully so the app can fetch the result.

To observe status changes, first register for the status by calling registerForFaceAuthStatus on onStart and then implement the ITSFaceAuthenticationStatus shown below on the MainActivity class of your app:

KotlinJava
Copy
Copied
class MainActivity : AppCompatActivity(), ITSFaceAuthenticationStatus {

    // Registers for status updates
    override fun onStart() {
        super.onStart()
        TSIdentityVerification.registerForFaceAuthStatus(this)

    }

    // Notifies when user has started to capture a selfie
    override fun faceAuthenticationStartCapturing() {

        Log.d("faceAuthDidReceiveStatus", "faceAuthCapturing")
    }

    // Notifies when user has finished uploading a selfie and face authentication is being processed
    override fun faceAuthenticationStartProcessing() {

        Log.d("faceAuthDidReceiveStatus", "faceAuthProcessing")
    }

    // Notifies when authentication process completed, and the result can be obtained via backend (Step 10)
    override fun faceAuthenticationCompleted() {

        Log.d("faceAuthDidReceiveStatus", "faceAuthCompleted")
    }

    // Notifies when user has canceled the process
    override fun faceAuthenticationCanceled() {

    Log.d("faceAuthDidReceiveStatus", "faceAuthCanceled")
    }

    // Notifies when verification error occurs
    override fun faceAuthenticationFail(error: TSIdentityVerificationError) {

        Log.d("faceAuthDidFail", error.name)
    }
}
Copy
Copied
public class MainActivity extends AppCompatActivity implements ITSFaceAuthenticationStatus {

    @Override
    protected void onStart() {

        // Registers for status updates
        super.onStart();
        TSIdentityVerification.registerForFaceAuthStatus(this);
    }

    // Notifies when user has started to capture a selfie
    @Override
    public void faceAuthenticationStartCapturing() {
        Log.d("faceAuthDidReceiveStatus", "faceAuthCapturing");
    }

    // Notifies when user has finished uploading a selfie and the face authentication is being processed
    @Override
    public void faceAuthenticationStartProcessing() {
        Log.d("faceAuthDidReceiveStatus", "faceAuthProcessing");
    }

    // Notifies when authentication process completed, and the result can be obtained via backend (Step 10)
    @Override
    public void faceAuthenticationCompleted() {
        Log.d("faceAuthDidReceiveStatus", "faceAuthCompleted");
    }

    // Notifies when user has canceled the process
    @Override
    public void faceAuthenticationCanceled() {

    Log.d("faceAuthDidReceiveStatus", "faceAuthCanceled")
    }

    // Notifies when verification error occurs
    @Override
    public void faceAuthenticationFail(TSIdentityVerificationError error) {
        Log.d("faceAuthDidFail", error.name());
    }
}

Step 5: Add camera permission

client

Your app requires camera permissions in order to capture the images required for the verification process.

  1. Open the AndroidManifest.xml file located in the app directory of your Android project.
  2. Add the following permission inside the <manifest> tag:
Copy
Copied
<uses-permission android:name="android.permission.CAMERA" />
  1. To handle camera permissions, add the following code snippet to the activity or fragment where you need to request camera permissions:
KotlinJava
Copy
Copied
// Check if the camera permission is granted
if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    // Camera permission granted, start face authentication here
} else {
    // Request camera permission
    ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.CAMERA), 101)
}

// Handle the result of the permission request
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
    if (requestCode == 101) {
        if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Camera permission granted, start face authentication here
        } else {
            // Camera permission denied, handle unauthorized state
        }
    }
}
Copy
Copied
// Check if the camera permission is granted
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
    // Camera permission granted, start face authentication here
} else {
    // Request camera permission
    ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.CAMERA}, 101);
}

// Handle the result of the permission request
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == 101) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Camera permission granted, start face authentication here
        } else {
            // Camera permission denied, handle unauthorized state
        }
    }
}

Step 6: Collect reference image

client

In the face authentication flow, a reference image is required to prove user's identity as Mosaic will compare the user's selfie against the reference. Your app should collect a reference image before starting the face authentication. This could be achieved by downloading the selfie created during the document verification flow, by fetching user ID photos from government databases, or using any other method of your choice.

For example, after a document verification flow, the selfie image can be retrieved by fetching all image IDs for the verification session (see Get all session images) and then fetching the selfie image itself by its ID (see Get image by ID).

Note

Mosaic stores document verification images for 90 days. Contact Mosaic to extend the retention period.

Step 7: Get access tokens

backend

Since the access token is required to authorize the backend API calls, such as for creating a verification session (Step 8) and obtaining the result (Step 10), your app should be able to obtain these tokens from Mosaic.

Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const formData = {
    client_id: '[CLIENT_ID]', // Client ID obtained in Step 1
    client_secret: '[CLIENT_SECRET]', // Client secret obtained in Step 1
    grant_type: 'client_credentials',
    resource: 'https://verify.identity.security' // Targets IDV resource (required)
  };

  const resp = await fetch(
    `https://api.transmitsecurity.io/oidc/token`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      body: new URLSearchParams(formData).toString()
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();
Notes
  • The token must be requested for the https://verify.identity.security resource, which will appear in the audience claim of the generated token (in the future we’ll block access to tokens without this audience).
  • The token must remain secure on your server, and must only be used for backend requests.

Step 8: Create session

backend

Before your mobile app can initiate the face auth process, your backend must create a session in order to provide a secure context for the flow and submit a reference image. To do this, send a request like the one below (see API reference):

Note

For optimal results, the image resolution should be HD to FHD (~1900x1000).

Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const resp = await fetch(
    `https://api.transmitsecurity.io/verify/api/v1/face-auth`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]' // Client access token generated in Step 7
      },
      body: JSON.stringify({
        reference: {
          type: 'raw',
          content: '[IMAGE_CONTENT]',  // Content of the image, as a base64 string
          format: 'jpg' // Currently only jpeg is supported
        }
      })
    }
  );

  const data = await resp.json();
  console.log(data);
}

run();

The response contains device_session_id that will be used to start the face authentication session on the client-side (in Step 9), and obtain the result (in Step 10). For example:

Copy
Copied
{
  "device_session_id": "ca766ed78c8c0b7824dfea356ed30b72",
  "session_id": "H1I12oskjzsdhskj4"
}

Step 9: Start session

client

Once a session is created, initiate the authentication process using the startFaceAuth() SDK method. Add the code below to your mobile app, passing the device_session_id value returned in the previous step. If successful, the SDK will start a face authentication process for the user and guide them through the flow using the Mosaic experience.

KotlinJava
Copy
Copied
// Starts a face authentication process using the device session created in the backend
TSIdentityVerification.startFaceAuth(context, "DEVICE_SESSION_ID")
Copy
Copied
// Starts a face authentication process using the device session created in the backend
TSIdentityVerification.startFaceAuth(context, "DEVICE_SESSION_ID");

Step 10: Get results

backend

Once the face authentication process starts, your mobile app can track its status using the extension added in Step 4. When the selfie is successfully submitted, Mosaic starts processing the verification and the SDK starts polling for the status. If the status is completed, your backend should send the request below to obtain the verification result (see API reference):

Copy
Copied
import fetch from 'node-fetch';

async function run() {
  const dsid = 'YOUR_dsid_PARAMETER'; // Device session ID returned in Step 8
  const resp = await fetch(
    `https://api.transmitsecurity.io/verify/api/v1/face-auth/${dsid}/result`,
    {
      method: 'GET',
      headers: {
        Authorization: 'Bearer [CLIENT_ACCESS_TOKEN]' // Client access token generated in Step 7
      }
    }
  );

  const data = await resp.text();
  console.log(data);
}

run();

Step 11: Handle results

client

Your app should decide how to proceed based on the face authentication result returned in the previous step, which is indicated by the recommendation field:

  • If ALLOW : the face authentication process was completed successfully. The user identity is confirmed.
  • If CHALLENGE : the face authentication process didn't succeed, since at least one verification check didn't pass. Depending on your use case, proceed with other checks.
  • If DENY : the face authentication indicates a high likelihood of attempted fraud. You should block the user or initiate an in-depth review to avoid fraudulent actions.

Here's an example response for a successful face authentication:

Copy
Copied
{
  "status": "complete",
  "recommendation": "ALLOW"
}