Face Login

This guide explains how to use the face login feature in the Welcome SDK.

Prerequisites

First, the user has to be an approved customer by having completed the onboarding flow. Check the image below for more information:

Onboarding and Login

Initialize Incode Welcome SDK as described here.

1:N Face Login - Identify a user

1:N Face login is suitable if you would like to identify a user by doing a face scan. This will execute a face match comparison across your entire user database and checks if the face corresponds to any of the approved users.

To execute 1:N Face Login call startFaceLogin method:

IncodeWelcome.getInstance()
    .startFaceLogin(activityContext, SelfieScan.Builder().build(), object : SelfieScanListener {
        override fun onSelfieScanCompleted(selfieScanResult: SelfieScanResult) {
            if (selfieScanResult.status == SelfieScanResult.STATUS_OK) {
                val faceLoginResult = selfieScanResult.faceLoginResult
                if (faceLoginResult != null && faceLoginResult.success) {
                    val customerUUID = faceLoginResult.customerUUID
                    val token = faceLoginResult.token
                    val interviewId = faceLoginResult.interviewId
                    // ...
                } else {
                    val isSpoofAttempt = selfieScanResult.isSpoofAttempt ?: false
                    if (isSpoofAttempt) {
                        // Liveness failed
                    } else {
                        // User's face not found
                    }
                }
            } else {
                // Some of the preconditions for checking liveness failed, check selfieScanResult.status for specific info
            }
        }

        override fun onError(error: Throwable?) {
            // Some error occurred
        }

        override fun onUserCancelled() {
            // User canceled login
        }
    })
IncodeWelcome.getInstance()
    .startFaceLogin(activityContext, new SelfieScan.Builder().build(), new SelfieScanListener() {
        @Override
        public void onSelfieScanCompleted(SelfieScanResult selfieScanResult) {
            if (selfieScanResult.status == SelfieScanResult.STATUS_OK) {
                FaceLoginResult faceLoginResult = selfieScanResult.faceLoginResult;
                if (faceLoginResult != null && faceLoginResult.success) {
                    String customerUUID = faceLoginResult.customerUUID;
                    String token = faceLoginResult.token;
                    String interviewId = faceLoginResult.interviewId;
                    // ...
                } else {
                    boolean isSpoofAttempt = selfieScanResult.isSpoofAttempt != null ? selfieScanResult.isSpoofAttempt : false;
                    if (isSpoofAttempt) {
                        // Liveness failed
                    } else {
                        // User's face not found
                    }
                }
            } else {
                // Some of the preconditions for checking liveness failed, check selfieScanResult.status for specific info
            }
        }

        @Override
        public void onError(Throwable error) {
            // Some error occurred
        }
    
        @Override
        public void onUserCancelled() {
            // User canceled login
        }
    });

1:1 Face Login - Verify the face of a specific user

1:1 Face Login is suitable if you want do a face authentication for a specific user that is already pre-authorized, meaning you already have the customer's UUID and only want to know if this exact person is trying to authenticate.

To execute 1:1 Face Login call startFaceLogin method:

val selfieScanListener = object : SelfieScanListener {
    override fun onSelfieScanCompleted(selfieScanResult: SelfieScanResult) {
        when (selfieScanResult.status) {
            SelfieScanResult.STATUS_OK -> {
                val faceLoginResult = selfieScanResult.faceLoginResult
                if (faceLoginResult != null && faceLoginResult.success) {
                    val customerUUID = faceLoginResult.customerUUID
                    val token = faceLoginResult.token
                    val interviewId = faceLoginResult.interviewId
                    // ...
                } else {
                    val isSpoofAttempt = selfieScanResult.isSpoofAttempt ?: false
                    if (isSpoofAttempt) {
                        // Liveness failed
                    } else {
                        // User's face is not similar to previously enrolled face
                    }
                }
            }
            else -> {
                // Some of the preconditions for checking liveness failed, check selfieScanResult.status for specific info
            }
        }
    }

    override fun onError(error: Throwable?) {
        // Some error occurred
    }

    override fun onUserCancelled() {
        // User canceled login
    }
}

// ...

IncodeWelcome.getInstance()
    .startFaceLogin(
        activityContext,
        SelfieScan.Builder().setCustomerUUID("YOUR_CUSTOMER_ID").build(),
        selfieScanListener
    )
IncodeWelcome.getInstance()
    .startFaceLogin(activityContext, new SelfieScan.Builder().setCustomerUUID("YOUR_CUSTOMER_ID").build(), new SelfieScanListener() {
        @Override
        public void onSelfieScanCompleted(SelfieScanResult selfieScanResult) {
            if (selfieScanResult.status == SelfieScanResult.STATUS_OK) {
                FaceLoginResult faceLoginResult = selfieScanResult.faceLoginResult;
                if (faceLoginResult != null && faceLoginResult.success) {
                    String customerUUID = faceLoginResult.customerUUID;
                    String token = faceLoginResult.token;
                    String interviewId = faceLoginResult.interviewId;
                    // ...
                } else {
                    boolean isSpoofAttempt = selfieScanResult.isSpoofAttempt != null ? selfieScanResult.isSpoofAttempt : false;
                    if (isSpoofAttempt) {
                        // Liveness failed
                    } else {
                        // User's face is not similar to previously enrolled face
                    }
                }
            } else {
                // Some of the preconditions for checking liveness failed, check selfieScanResult.status for specific info
            }
        }

        @Override
        public void onError(Throwable error) {
            // Some error occurred
        }

        @Override
        public void onUserCancelled() {
            // User canceled login
        }
    });

Face Login result

The resulting SelfieScanResult object will have the following:

  • faceLoginResult - FaceLoginResult object that contains:
    • success - True if face login was successful, false otherwise
    • customerUUID - Customer UUID of the matched user, null otherwise
    • token - Customer token of the matched user, null otherwise
    • interviewId - Session interviewId from which the user got approved
    • interviewToken - Session interviewToken which was used during user approval
    • transactionId - Transaction ID of the face login attempt
  • isSpoofAttempt - Specifies if there was a spoof attempt or not
  • croppedSelfieImgPath - Cropped selfie image location taken during the selfie scan step
  • fullFrameSelfieImgPath - Full frame selfie image location taken during the selfie scan step
  • selfieEncryptedBase64 - Encrypted Base64-encoded selfie image
  • selfieBase64 - Base64-encoded selfie image
  • isFaceMatched - True if face login was successful, false otherwise. Same information as faceLoginResult.success
  • hasFaceMask - True if a face mask is detected, false otherwise. Null if a check wasn't performed
  • error - Throwable that describes the error that happened during the face login
  • resultCode - Result code common to all IncodeWelcome results

Login parametrization

By default, Face Login will complete a liveness check and face match on the server, and we highly suggest switching to on-device liveness check and face match in order to improve the speed of Face Login and reduce network traffic, which is particulary important for devices that can have unstable network connections.

To switch to on-device liveness:

  1. In your module-level app/build.gradle, add additional Incode dependencies:
dependencies {
    ...
    implementation 'com.incode.sdk:model-liveness-detection:3.0.0'
    implementation 'com.incode.sdk:model-face-recognition:3.0.0'
}
  1. Specify FaceAuthMode.HYBRID when creating SelfieScan module:
val selfieScan = SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.HYBRID)
    .build()
SelfieScan selfieScan = new SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.HYBRID)
    .build();

To switch to on-device liveness and on-device face recognition include:

  1. In your module-level app/build.gradle, add an additional Incode dependency:
dependencies {
    ...
    implementation 'com.incode.sdk:model-liveness-detection:3.0.0'
    implementation 'com.incode.sdk:model-face-recognition:3.0.0'
}
  1. Specify FaceAuthMode.LOCAL when creating SelfieScan module
val selfieScan = SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.LOCAL)
    .build()
SelfieScan selfieScan = new SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.LOCAL)
    .build();

Note: SelfieScan.FaceAuthMode.LOCAL can work only in 1:1 mode and if the user's face template is already saved locally in the device. Templates get saved during successful onboarding or by using the allowFaceAuthModeFallback parameter (please check explanation below).

Other parameters to consider:

  • setCustomerUUID: ID that uniquely identifies the pre-authorized user who is performing a face login. When set, 1:1 login is performed. When this value is null, 1:N login is performed.
  • setShowTutorials: Show tutorials how to capture selfie before the actual scan. true by default.
  • setWaitForTutorials: Hide the continue button in the tutorial screen while tutorial animation is playing, in order to make sure the user has seen the video before continuing. true by default.
  • setAllowFaceAuthModeFallback - Specify true if you would want to do a SelfieScan.FaceAuthMode.SERVER face login in case SelfieScan.FaceAuthMode.LOCAL couldn't be performed due to missing the face template on the device. This is applied only to 1:1 Face login.
  • setLensesCheckEnabled: Set to false if you would like to disable lenses detection during selfie scan. true by default.
  • setMaskCheckEnabled: Specify true to enable face mask check detection during face capture. false by default. The face mask check is performed on the server, unless a local mask detection dependency is included in the module-level app/build.gradle.
  • setBrightnessThreshold: Adjust the minimum requirements for a well-lit face during capture. Increasing the value will be more restrictive and require better lighting conditions, decreasing the value will loosen requirements and allow capturing while being in a darker environment. You can set the value to 0 to ignore this setting. The value is set at 50 by default.
  • setCameraFacing: Sets the direction that the camera faces. It can be CameraFacing.FRONT or CameraFacing.BACK. CameraFacing.FRONT by default.
  • setLogAuthenticationEnabled: Specify false if, for example, you want to make sure that no network calls are being performed in SelfieScan.FaceAuthMode.LOCAL mode. Note that the authentication attempts won't be visible in the dashboard. true by default.
  • setAssistedOnboardingEnabled: Enables assisted onboarding (back-camera-only onboarding). This is equivalent to setCameraFacing(CameraFacing.BACK).

Kiosk Login

Kiosk Login is a special face login mode for shared devices with large screens, e.g. tablets. This mode is optimized for a faster login experience. The main factors which accelerate this are:

  1. Liveness check is disabled.
  2. Face recognition is done on the server, but without the need to upload a photo. The face template is generated on the device and sent to the server. The server does the match based on the template. The template is significantly smaller than the photo, speeding up the network communication.
  3. Kiosk Login has a special UX with a full screen camera and relaxed face position constrains.

In order to use Kiosk Login, specify FaceAuthMode.FR_ONLY when creating the SelfieScan module:

val selfieScan = SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.FR_ONLY)
    .build()
SelfieScan selfieScan = new SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.FaceAuthMode.FR_ONLY)
    .build();

QR Face Login

QR Face Login mode is based on offline authentication. It works by matching the face against the face template contained in the QR code. This mode is optimized for a faster login experience. The main factors which accelerate this are:

  1. Liveness check is disabled.
  2. Face recognition is done on the device.

In order to use QR Face Login, specify Mode.QR_FACE_LOGIN when creating the SelfieScan module:

val selfieScan = SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.Mode.QR_FACE_LOGIN)
    .build()
SelfieScan selfieScan = new SelfieScan.Builder()
    .setFaceAuthMode(SelfieScan.Mode.QR_FACE_LOGIN)
    .build();

Execute QR Face Login call startQrFaceLogin method:

IncodeWelcome.getInstance().startQrFaceLogin(activityContext, selfieScan, object : QrFaceLoginListener {
    override fun onQrFaceLoginCompleted(qrFaceLoginResult: QrFaceLoginResult) {
        if (qrFaceLoginResult.isFaceMatched) {
            // QR Face login succeeded
        } else {
            // QR Face login failed
        }
    }

    override fun onError(error: Throwable?) {
        // Some error occurred
    }

    override fun onUserCancelled() {
        // User canceled qr face login
    }
})
IncodeWelcome.getInstance().startQrFaceLogin(activityContext, selfieScan, new QrFaceLoginListener() {
    @Override
    public void onQrFaceLoginCompleted(QrFaceLoginResult qrFaceLoginResult) {
        if (qrFaceLoginResult.isFaceMatched) {
            // QR Face login succeeded
        } else {
            // QR Face login failed
        }
    }
    
    @Override
    public void onError(Throwable error) {
        // Some error occurred
    }
    
    @Override
    public void onUserCancelled() {
        // User canceled qr face login
    }
});