Authentication methods
Encap supports several different authentication methods. Not every authentication method can be used on every platform.
Below you will find:
- The supported authentication methods for Android.
- How to use these authentication methods.
- The names of the parameters that you need in order to activate and authenticate with these methods.
The number of authentication methods could be limited by:
- The application configuration on the Encap server, identified by the
applicationId. - The mobile device's hardware.
- The mobile device's state.
Device
The device is a single-factor authentication method. You have the option to request single-factor device activation and authentication.
This is indicated in the Encap client API in the following objects:
StartActivationResultStartAuthenticationResult
You can access this through the lists returned by the following methods:
getAuthMethodsForActivation()getAuthMethodsForAuthentication()
The Encap client decides whether to complete the activation or authentication request with single or two factors.
- For single-factor activation, it passes
DeviceActivationParametertofinishActivation(). - For single-factor authentication, it passes
DeviceAuthParametertofinishAuthentication().
A single-factor authentication method cannot be used to add a two-factor authentication method.
You need to configure on the server which two-factor authentication methods can be used to add other two-factor authentication methods.
When you activate a two-factor authentication method, Encap will always activate the device as well.
There are only a few use cases where activating only the device makes sense, such as with a pure authenticator app.
PIN
Activation
We recommend that you activate the PIN first so that you will always have a backup. This could be useful if:
- Other authentication methods get invalidated or temporarily locked.
- The user cancels Biometrics, if it is not possible for them to use it at that time.
You can add Biometrics at a later point using the addOrUpdate operation, as described in the Add or update section for Biometric Prompt.
Activating the PIN is similar to activating a device. However, you have to set the PIN on the activation parameter object.
The app is responsible for making the UI where the user can set a PIN. The StartActivationResult operation can be used to present the UI in the correct way, in terms of:
- Password type.
- Password length.
- Keyboard type.
You can get this information using:
- Kotlin
- Java
// Activation code should come from an other source, for example a web page.
controller.startActivation(activationCode, object : AsyncCallback<StartActivationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartActivationResult) {
// Ready to continue to finishActivate. See result on how to proceed.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
val activationParameter = DevicePinActivationParameter(pinCode)
controller.finishActivation(activationParameter, object : AsyncCallback<FinishActivationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishActivationResult) {
// The authentication method is activated.
}
})
}
})
// Activation code should come from an other source, for example a web page.
controller.startActivation(activationCode, new AsyncCallback<StartActivationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartActivationResult result) {
// Ready to continue to finishActivate. See result on how to proceed.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
DevicePinActivationParameter activationParameter = new DevicePinActivationParameter(pinCode);
controller.finishActivation(activationParameter, new AsyncCallback<FinishActivationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishActivationResult result) {
// The authentication method is activated.
}
});
}
});
You should not store the end-user's PIN anywhere.
Authentication
Authenticating with a PIN is similar to activating a PIN. However, you use the startAuthentication and finishAuthetication operations instead.
The results of StartAuthenticationResult gives you the same information as in activation (StartActivationResult), so that you can set up the UI for PIN input.
- Kotlin
- Java
controller.startAuthentication(object : AsyncCallback<StartAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartAuthenticationResult) {
// Ready to continue to finishAuthentication. See result on how to proceed.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
val authParameter = DevicePinAuthParameter(pinCode)
controller.finishAuthentication(authParameter, object : AsyncCallback<FinishAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishAuthenticationResult) {
// The authentication method is activated.
}
})
}
})
controller.startAuthentication(new AsyncCallback<StartAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartAuthenticationResult result) {
// Ready to continue to finishAuthentication. See result on how to proceed.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
DevicePinAuthParameter authParameter = new DevicePinAuthParameter(pinCode);
controller.finishAuthentication(authParameter, new AsyncCallback<FinishAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishAuthenticationResult result) {
// The authentication method is activated.
}
});
}
});
Add or update (change PIN)
You can use the addOrUpdate operation to update the PIN. This looks similar to authenticating with a PIN, however, you have to set the old PIN on the AuthParameter, and the new PIN on the ActivationParameter.
- Kotlin
- Java
controller.startAddOrUpdate(object : AsyncCallback<StartAddOrUpdateResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartAddOrUpdateResult) {
// Old and new PIN needs to come from the apps UI, usually between the start and finish call.
val authParameter = DevicePinAuthParameter(oldPin)
val activateParameter = DevicePinActivationParameter(newPin)
controller.finishAddOrUpdate(authParameter, activateParameter, object : AsyncCallback<FinishAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishAuthenticationResult) {
// Successfully changed the PIN.
}
})
}
})
controller.startAddOrUpdate(new AsyncCallback<StartAddOrUpdateResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartAddOrUpdateResult result) {
// Old and new PIN needs to come from the apps UI, usually between the start and finish call.
AuthParameter authParameter = new DevicePinAuthParameter(oldPin);
ActivationParameter activateParameter = new DevicePinActivationParameter(newPin);
controller.finishAddOrUpdate(authParameter, activateParameter, new AsyncCallback<FinishAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishAuthenticationResult result) {
// Successfully changed the PIN.
}
});
}
});
BiometricPrompt
Overview
Biometric Prompt was introduced in Android 9.
The Encap Android API 3.13 and higher supports the AndroidX Biometric support library, which is a recommended support library from Google that offers backward compatibility for Biometric Prompt.
This can simplify the implementation for an app, as you can implement Biometric Prompt for all devices instead of implementing Fingerprint from Android 6-8, and then Biometric Prompt for Android 9 and higher.
When the AndroidX Biometric library dependency is added to the app, the SDK will pick this up and switch the implementation; making BiometricPrompt available for Android 6 and later.
If the dependency is not added, then you can only use Biometric Prompt for Android 9 and later.
How to implement
- You must enable
DEVICE_ANDROID_BIOMETRIC_PROMPTand an authentication method in the application configuration. - Add the following entries to
AndroidManifestof the app:<uses-permission android:name="android.permission.USE_BIOMETRIC" /> - Optional: If you want to get Biometric Prompt for older devices as well, then you must add the following dependency to the Gradle build file:
- Gradle (Kotlin)
- Gradle (Groovy)
implementation("androidx.biometric:biometric:1.1.0")implementation "androidx.biometric:biometric:1.1.0"
The DeviceAndroidBiometricPromptActivationParameter and DeviceAndroidBiometricPromptAuthParameter classes have the following constructors:
- Two constructors with a
FragmentorFragmentActivityparameter. You can use these with the AndroidX Biometric Prompt library. - Other constructors without a
FragmentorFragmentActivityparameter. You can use these with the native Android BiometricPrompt.
If you use the wrong constructor, you will get an exception.
Activation
Although we recommend that you activate a PIN first so that you have a fallback, it is possible to activate Biometrics directly. Before you can set this up:
- Biometrics need to be added as an authentication method in the application configuration on the Encap server, identified by the
applicationId. - Biometrics need to be enabled on the mobile device.
To verify whether you can activate Biometrics, you can check StartActivationResult.getAuthMethodForActivation to see if the list contains AuthMethod.DEVICE_ANDROID_BIOMETRIC_PROMPT. If it does, then you are ready to finish the activation.
The activation parameters for Biometric Prompt differ from device and PIN, and require more data to be filled out. You can control the following:
- Title (required)
- Subtitle
- Description
- Name of the negative button on the dialogue (required)
The constructor of the ActivationParameter differs depending on if you are using the AndroidX Biometric prompt library or not:
- If you are using the library, then you should use the constructor with
FragmentorFragmentActivity. - If you are not using the library, then use the constructor without
FragmentorFragmentActivity.
- Kotlin
- Java
// Activation code should come from another source, for example a web page.
controller.startActivation(activationCode, object : AsyncCallback<StartActivationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartActivationResult) {
// Ready to continue to finishActivate. See result on how to proceed.
val activationParameter = DeviceAndroidBiometricPromptActivationParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel")
controller.finishActivation(activationParameter, object : AsyncCallback<FinishActivationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishActivationResult) {
// The authentication method is activated.
}
})
}
})
// Activation code should come from another source, for example a web page.
controller.startActivation(activationCode, new AsyncCallback<StartActivationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartActivationResult result) {
// Ready to continue to finishActivate. See result on how to proceed.
DeviceAndroidBiometricPromptActivationParameter activationParameter = new DeviceAndroidBiometricPromptActivationParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel");
controller.finishActivation(activationParameter, new AsyncCallback<FinishActivationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishActivationResult result) {
// The authentication method is activated.
}
});
}
});
Activating Biometrics requires the user to authenticate. This differs from iOS where it is not needed during activation.
It is technically possible to turn the dialogue off on the Android platform, but we do not expose that possibility in our Encap library. This would lower the security, as the key would not be bound to the Biometrics.
Authentication
Authenticating with Biometrics is similar to activating with Biometrics. However, you use the startAutentication and finishAuthentication instead, with DeviceAndroidBiometricPromptAuthParameter as the AuthParameter.
First, you need to make sure that Biometrics is a possible authentication method. This means that it is:
- Configured on the server.
- Activated and enabled on the phone.
- In a state where it can be used.
To verify this, you can check StartAuthenticationResult.getAuthMethodForAuthentication() to see if the list contains AuthMethod.DEVICE_ANDROID_BIOMETRIC_PROMPT.
Just as with activation for Biometric Prompt, you need to fill out some additional information for the authentication parameters (AuthParameter). You can control the following:
- Title (required)
- Subtitle
- Description
- Name of the negative button on the dialogue (required)
- Kotlin
- Java
controller.startAuthentication(object : AsyncCallback<StartAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartAuthenticationResult) {
// Ready to continue to finishAuthentication. See result on how to proceed.
val authParameter = DeviceAndroidBiometricPromptAuthParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel")
controller.finishAuthentication(authParameter, object : AsyncCallback<FinishAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishAuthenticationResult) {
// The authentication method is activated.
}
})
}
})
controller.startAuthentication(new AsyncCallback<StartAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartAuthenticationResult result) {
// Ready to continue to finishAuthentication. See result on how to proceed.
DeviceAndroidBiometricPromptAuthParameter authParameter = new DeviceAndroidBiometricPromptAuthParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel");
controller.finishAuthentication(authParameter, new AsyncCallback<FinishAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishAuthenticationResult result) {
// The authentication method is activated.
}
});
}
});
Add or update
A typical use case is to:
- Activate a PIN as a fallback, as described in the Activation section for PIN.
- Use the
addOrUpdateoperation to add Biometrics.
If you want to add a new authentication method, then you need to have a two-factor authentication method already registered.
You can add Biometrics from any two-factor authentication method, as long as it is configured in the application configuration on the Encap server, identified by the applicationId. PIN is the most commonly used authentication method for adding Biometrics.
You can verify whether you have a two-factor authentication method that can add Biometrics by checking the getAuthMethodForAuthentication() function on the returned StartAddOrUpdateResult object.
- Kotlin
- Java
controller.startAddOrUpdate(object : AsyncCallback<StartAddOrUpdateResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: StartAddOrUpdateResult) {
// Old and new PIN needs to come from the apps UI, usually between the start and finish call.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
val authParameter = DevicePinAuthParameter(pinCode)
val activationParameter = DeviceAndroidBiometricPromptActivationParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel")
controller.finishAddOrUpdate(authParameter, activationParameter, object : AsyncCallback<FinishAuthenticationResult> {
override fun onFailure(errorCodeException: ErrorCodeException) {
// Handle the failure.
}
override fun onSuccess(result: FinishAuthenticationResult) {
// Successfully added Biometrics using PIN.
}
})
}
})
controller.startAddOrUpdate(new AsyncCallback<StartAddOrUpdateResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(StartAddOrUpdateResult result) {
// Old and new PIN needs to come from the apps UI, usually between the start and finish call.
// PIN should have been typed into the UI of the app, usually between the start and finish call.
DevicePinAuthParameter authParameter = new DevicePinAuthParameter(pinCode);
DeviceAndroidBiometricPromptActivationParameter activationParameter = new DeviceAndroidBiometricPromptActivationParameter(context, fragment)
.setTitle("MyTitle")
.setSubtitle("MySubtitle")
.setDescription("MyDescription")
.setNegativeButtonText("Cancel");
controller.finishAddOrUpdate(authParameter, activationParameter, new AsyncCallback<FinishAuthenticationResult>() {
public void onFailure(ErrorCodeException errorCodeException) {
// Handle the failure.
}
public void onSuccess(FinishAuthenticationResult result) {
// Successfully added Biometrics using PIN.
}
});
}
});
Error handling
The error handling for Biometric Prompt is similar to regular authentication, however, you have to consider actions for the error codes of failed Biometric authentications in addition.
The isRecoverableError() operation can be used to find out whether finishAuthentication() can be called again.
If any of the following occur, then the Encap authentication data is invalidated and a re-activation is required:
- Biometrics are added to the device.
- All Biometrics are removed from the device.
- The secure lock screen is disabled.
See error code clientErrorAuthDataInvalidated for more details.
Fall back to PIN
A fall back to PIN could provide a better user experience for your end-user in some use cases, instead of just failing the authentication. For example:
- If the end-user cancels Biometric Prompt.
- If the end-user uses the incorrect finger too many times.
How to implement
- First, a PIN needs to be activated. You can verify whether PIN is an option to fall back to by looking for
DEVICE_PIN:- Kotlin
- Java
Example: Look for DEVICE_PINstartAuthenticationResult.authMethodsForAuthentication.contains(DEVICE_PIN)Example: Look for DEVICE_PINstartAuthenticationResult.getAuthMethodsForAuthentication().contains(DEVICE_PIN); - Next, you need to look for the correct error codes in the
OnFailurescenario infinishAuthenticaion:- If you only want to fall back to PIN if Biometrics have been locked, then you need to check if the error code corresponds to either:
clientErrorAndroidBiometricPromptErrorLockoutclientErrorAndroidBiometricPromptLockoutPermanent
- If you also want to fall back to PIN if the end-user cancels the Biometric Prompt, then you also need to look for:
clientErrorAndroidBiometricPromptErrorCanceledclientErrorAndroidBiometricPromptErrorUserCanceled
- Kotlin
- Java
Example: Look for correct error codes// In OnFailure
if (canFallbackToPin() && (isLockedError(errorCode) || isCanceledError(errorCode))) {
// Go to the PIN flow.
// Calling startAuthentication is not needed, as we are in a retryable state.
}Example: Look for correct error codes// In OnFailure
if (canFallbackToPin() && (isLockedError(errorCode) || isCanceledError(errorCode))) {
// Go to the PIN flow.
// Calling startAuthentication is not needed, as we are in a retryable state.
} - If you only want to fall back to PIN if Biometrics have been locked, then you need to check if the error code corresponds to either:
If the end-user uses the wrong finger too many times, then Biometrics will be locked or permanently locked. This is only a local state, and is resolved on the phone itself.
Most phones will unlock Biometrics after turning off the screen, then using a PIN or Pattern to unlock it again.
Confirmation Required
On some mobile devices, it is possible to control whether you want a button to confirm that you are authenticating with your face or not. This confirmation button is designed to prevent accidental authentication by just looking at the screen when the Biometric Prompt appears.
The activation and authentication parameter classes for BiometricPrompt are as follows:
DeviceAndroidBiometricPromptActivationParameterDeviceAndroidBiometricPromptAuthParameter
In these classes, you can set a hint to override this setting on the mobile device. To do this, you have to set setConfirmationRequired() to either true or false.
The default value is true. It is not recommended to set to false for high-value transactions.
This is just a hint to the system. The system could still ignore the hint and:
- Show the confirmation button even though the hint is set to
false. - Show no button even if the hint is set to
true.
An alternative to this Android feature is to implement the confirmation button in your UI yourself before calling finishAuthentication() or finishActivation().
Multiple controller scenario
If you are activating multiple registrations, but only want the end-user to confirm with Biometrics once (to activate Biometrics for all of the registrations), then you can use setSingleBiometricPromptEnabled().
To do this:
- For the first
DeviceAndroidBiometricPromptActivationParameterobject, set this tofalse. - For the second and following controllers, set it to
true.
This will allow finishActivation() to complete the activation without interaction from the end-user for the following controllers.
This has to be done within a 60 seconds time period for it to work.