Secondary Biometric Authentication
Document Description
This document describes how the Android client can integrate biometric authentication. Biometric authentication means using the mobile phone's fingerprint / face recognition as multi-factor authentication when logging in on a PC.
When the user logs in on a PC and selects "Use Authenticator as Secondary Authentication", the user opens the APP at the same time. Ensuring login status and that the device is bound, the App queries the back end for secondary authentication requests. If any, the APP invokes the phone's biometric recognition to confirm the device owner, and notifies the IDaaS server to proceed with the authentication authorization flow.
Flow Description

Device Binding Integration Flow Description
- The App client calls the method to query the device binding status. The IDaaS SDK requests the IDaaS server to query whether the device is bound.
- If the query result shows that this device is not bound, the App client calls the device binding method. The IDaaS SDK initiates the binding flow, invokes the phone's fingerprint / face recognition to confirm the device owner. The IDaaS server records this device as bound and returns binding success/failure to the App client.
Secondary Authentication Flow Description
- The user opens a PC browser to log in to the IDaaS system, selects "Use Authenticator as Secondary Authentication", and taps Verify. The App client calls the method to query whether there is a secondary authentication request. The IDaaS server returns a random string.
- The App client obtains the random string and uses it to call the secondary authentication method. The IDaaS SDK initiates the secondary authentication flow, invokes the phone's fingerprint / face recognition to confirm the device owner, and sends the secondary authentication request to the IDaaS server. After server verification passes, the PC web page automatically refreshes its status.
Unbinding Flow Description
- The App client calls the method to query the device binding status. The IDaaS SDK requests the IDaaS server to query whether the device is bound.
- If the query result shows that this device is bound, the App client calls the unbinding method. The IDaaS SDK initiates the unbinding flow, invokes the phone's fingerprint / face recognition to confirm the device owner. The IDaaS server records this device as unbound and returns unbinding success/failure to the App client.
Preparation
Obtain clientID
Log in to the IDaaS Enterprise Center, click Resources > Applications, and select the relevant application to view it. 
Enable Secondary Authentication
Log in to the IDaaS Enterprise Center, click Authentication > Authentication Policy.

Tap Add Policy. A policy box will pop up on the right. Enter the description, check "Secondary Authentication" and "BambooCloud Authenticator (Face ID or Fingerprint)", and tap OK.

After the above configuration is complete, on the login user center page, after the first-level login, the option "Verify via BambooCloud Authenticator" will appear where secondary authentication is required.

Import Dependency Packages
AuthnCenter_Common-1.5.3.aar
AuthnCenter_MFA_BioVerify-1.5.3.aarDrag the IDaaS SDK into the project. The import method is as follows: 
Configure build.gradle
/*begin*/
/* rxjava2 + okhttp + retrofit2 */
api 'io.reactivex.rxjava2:rxjava:2.2.10'
api 'io.reactivex.rxjava2:rxandroid:2.1.1'
api 'com.squareup.retrofit2:retrofit:2.6.0'
api 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
api 'com.squareup.retrofit2:converter-gson:2.6.0'
api 'com.squareup.okhttp3:okhttp:4.3.1'
api 'com.squareup.okhttp3:logging-interceptor:3.6.0'
api 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
api 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
api 'com.alibaba:fastjson:1.2.61'
api 'org.greenrobot:greendao:3.3.0' // add library
implementation 'com.github.bumptech.glide:glide:4.14.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'
/*end */
api 'io.jsonwebtoken:jjwt-api:0.11.5'
runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
runtimeOnly('io.jsonwebtoken:jjwt-orgjson:0.11.5') {
exclude group: 'org.json', module: 'json' //provided by Android natively
}
implementation "androidx.biometric:biometric:1.1.0"Development Integration
SDK Initialization
AuthnCenterSDK.Builder()
.init(this)
.setBaseUrl("https://xxx.xxx.com") // Tenant domain
.setClientId("xxxx") // Tenant console application client-id
.isCheckSSL(false) // Whether to check SSL certificate
.logEnable(false).build(); // Whether to enable HTTP request logging. It is recommended to disable it after going live.Call Sequence Instructions
The SDK contains encapsulated biometric recognition interfaces and single business interfaces. The caller can flexibly combine them according to actual business needs.
Business interface call sequence instructions:
Query binding status --> Query whether there is authentication --> Start authentication.
Business Interface Call Examples
Query Device Binding Status
AuthnCenterMFA.Builder().getStatus(this, accountInfo.getIdToken(), new MFAListener<Boolean>() {
@Override
public void success(Boolean data) {
}
@Override
public void error(String s, String s1) {
ToastUtils.ShowToast(InfoActivity.this, String.format("%s %s", s, s1));
}
});Bind Device
try {
byte[] publicKey = keyStoreUtils.getPublicKey(accountInfo.getUserId()).getEncoded();
String pk = Base64.encodeToString(publicKey, Base64.DEFAULT).replaceAll("\n", "");
Map map = new HashMap();
map.put("publicKey", keyStoreUtils.PublicKeyToPem(pk));
AuthnCenterMFA.Builder().bind(this, accountInfo.getIdToken(), map, new MFAListener<Boolean>() {
@Override
public void success(Boolean o) {
ToastUtils.ShowToast(InfoActivity.this, o == true ? "Binding succeeded" : "Binding failed");
}
@Override
public void error(String s, String s1) {
ToastUtils.ShowToast(InfoActivity.this, String.format("%s %s", s, s1));
}
});
} catch (Exception e) {
ToastUtils.ShowToast(InfoActivity.this, String.format("%s", e.toString()));
e.printStackTrace();
}Unbind Device
AuthnCenterMFA.Builder().unbind(this, accountInfo.getIdToken(), new MFAListener<Boolean>() {
@Override
public void success(Boolean data) {
tv_switch.setChecked(data == true ? false : true);
ToastUtils.ShowToast(InfoActivity.this, data == true ? "Unbinding succeeded" : "Unbinding failed");
}
@Override
public void error(String s, String s1) {
ToastUtils.ShowToast(InfoActivity.this, String.format("%s %s", s, s1));
}
});Query Authentication Information
AuthnCenterMFA.Builder().getAuthentication(this, accountInfo.getIdToken(), new MFAListener<AuthenticationInfo>() {
@Override
public void success(AuthenticationInfo data) {
if (data != null && !TextUtils.isEmpty(data.getRandom())) {
} else {
return;
}
}
@Override
public void error(String code, String msg) {
}
});Start Authentication
try {
String singMsgTmp = String.format("%s", authenticationInfo.getRandom());
Signature signature = KeyStoreUtils.getInstance(getUserId(accountInfo.getIdToken())).initSignature();
signature.update(singMsgTmp.getBytes());
byte[] valueSign = signature.sign();
String sing = Base64.encodeToString(valueSign, Base64.DEFAULT).replaceAll("\n", "");
Map map = new HashMap();
map.put("signature", sing);
AuthnCenterMFA.Builder().startAuthentication(InfoActivity.this, accountInfo.getIdToken(), map, new MFAListener<Boolean>() {
@Override
public void success(Boolean data) {
if (data)
ToastUtils.ShowToast(mContext, "Authentication succeeded");
else
ToastUtils.ShowToast(mContext, "Authentication failed");
}
@Override
public void error(String code, String msg) {
ToastUtils.ShowToast(mContext, String.format("%s %s", code, msg));
}
});
} catch (Exception e) {
ToastUtils.ShowToast(mContext, String.format("%s", e.toString()));
}Biometric Interface Binding Call
AuthnCenterMFA.Builder().startBiometric(this, accountInfo.getIdToken(), BiometricType.BIND, new OnBiometricIdentifyCallback() {
@Override
public void onSucceeded() {
ToastUtils.ShowToast(InfoActivity.this, "Binding succeeded");
}
@Override
public void onFailed() {
}
@Override
public void onError(String s, String s1) {
ToastUtils.ShowToast(InfoActivity.this, "Binding failed" + s1);
}
@Override
public void onCancel() {
}
});Biometric Interface Unbinding Call
AuthnCenterMFA.Builder().startBiometric(this, accountInfo.getIdToken(), BiometricType.UNBIND, new OnBiometricIdentifyCallback() {
@Override
public void onSucceeded() {
ToastUtils.ShowToast(InfoActivity.this, "Unbinding succeeded");
}
@Override
public void onFailed() {
}
@Override
public void onError(String s, String s1) {
ToastUtils.ShowToast(InfoActivity.this, "Unbinding failed" + s1);
}
@Override
public void onCancel() {
}
});Error Codes
| HTTP Status | Error Code (error_code) | Error Message (error_msg) | Solution |
|---|---|---|---|
| 400 | IDAAS.SDK.COMMON.1001 | Parameter {0} cannot be left blank | |
| 400 | IDAAS.SDK.COMMON.1002 | The {0} parameter format is incorrect | |
| 400 | IDAAS.SDK.COMMON.1003 | Device information is incomplete | |
| 400 | IDAAS.SDK.COMMON.1004 | Signature decryption error | |
| 400 | IDAAS.SDK.COMMON.1005 | The {0} has failed | |
| 400 | IDAAS.SDK.COMMON.1006 | The {0} parameter error | |
| 400 | IDAAS.SDK.COMMON.1007 | The {0} parameter type error | |
| 500 | IDAAS.SDK.COMMON.1008 | The system is busy. Try again later | |
| 400 | IDAAS.SDK.COMMON.1009 | Unknown authentication configuration | |
| 400 | IDAAS.SDK.COMMON.1010 | Failed to obtain the enterprise center global configuration | |
| 400 | IDAAS.SDK.COMMON.1011 | Failed to obtain the international area code configuration | |
| 400 | IDAAS.SDK.COMMON.1012 | The x-client-ID is incorrect and the corresponding application cannot be found | |
| 400 | IDAAS.SDK.COMMON.1013 | The corresponding user is not found | |
| 400 | IDAAS.SDK.COMMON.1014 | Application private key not found | |
| 400 | IDAAS.SDK.LOGIN.1001 | Error calling interface | |
| 400 | IDAAS.SDK.LOGIN.1002 | User not bound | |
| 400 | IDAAS.SDK.LOGIN.1003 | The user has been locked due to too many unsuccessful login attempts. It will be unlocked in {0} minutes and {1} seconds | |
| 400 | IDAAS.SDK.LOGIN.1004 | Failed to obtain the password policy | |
| 400 | IDAAS.SDK.LOGIN.1005 | Invalid username or password. Remaining login attempts: | |
| 400 | IDAAS.SDK.LOGIN.1006 | Configuration error, unable to find wechat authentication source | |
| 400 | IDAAS.SDK.LOGIN.1007 | Configuration error, unable to find alipay authentication source | |
| 400 | IDAAS.SDK.LOGIN.1008 | The configuration is incorrect. The one-click login authentication source cannot be found | |
| 400 | IDAAS.SDK.SMS.1001 | {0} slide base map is not initialized successfully, please check the path | |
| 400 | IDAAS.SDK.SMS.1002 | {0} verification code coordinate resolution failed | |
| 400 | IDAAS.SDK.SMS.1003 | {0} verification code coordinate verification fails | |
| 400 | IDAAS.SDK.SMS.1004 | The graphic verification code is incorrect | |
| 400 | IDAAS.SDK.SMS.1005 | SMS verification code verification is incorrect | |
| 400 | IDAAS.SDK.SMS.1006 | The email verification code is incorrect | |
| 400 | IDAAS.SDK.SMS.1007 | Sending scenario does not exist | |
| 400 | IDAAS.SDK.SMS.1008 | Failed to send the verification code | |
| 400 | IDAAS.SDK.SOCIAL.1001 | The social account is unbound incorrectly | |
| 400 | IDAAS.SDK.SOCIAL.1002 | The social account has been bound, please unbind it first | |
| 400 | IDAAS.SDK.PWD.1001 | The password length is incorrect | |
| 400 | IDAAS.SDK.PWD.1002 | The password cannot be the username | |
| 400 | IDAAS.SDK.PWD.1003 | Your password complexity is low | |
| 400 | IDAAS.SDK.PWD.1004 | The password is weak | |
| 400 | IDAAS.SDK.PWD.1005 | The password is used before, cannot be used again | |
| 400 | IDAAS.SDK.PWD.1006 | Password cannot username in reverse order | |
| 400 | IDAAS.SDK.PWD.1007 | The number of repeated password characters exceeded the upper limit | |
| 400 | IDAAS.SDK.PWD.1008 | Password cannot contain :username, phone number, email prefix, name in PinYing | |
| 400 | IDAAS.SDK.MFA.1001 | The mobile doesn't match the user | |
| 400 | IDAAS.SDK.MFA.1002 | The access control policy is incorrect | |
| 400 | IDAAS.SDK.MFA.1003 | Access control authentication source type conversion error |