Skip to content

Commit

Permalink
Merge pull request #52 from akansha-maheshwari-axway/MOD-2588
Browse files Browse the repository at this point in the history
[MOD-2588] feat(android): add fallback support to password,pin or touch pattern
  • Loading branch information
lokeshchdhry authored Jul 23, 2020
2 parents 3fad899 + 39a4884 commit 22dc20f
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 34 deletions.
2 changes: 1 addition & 1 deletion android/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 3.0.1
version: 3.0.2
apiversion: 4
architectures: arm64-v8a armeabi-v7a x86 x86_64
description: titanium-identity
Expand Down
106 changes: 80 additions & 26 deletions android/src/ti/identity/FingerPrintHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,16 @@ public class FingerPrintHelper extends BiometricPrompt.AuthenticationCallback
private KrollObject krollObject;
protected boolean mSelfCancelled;
private boolean mGeneratedKey = false;
private TitaniumIdentityModule mModule;

@SuppressWarnings("NewApi")
public FingerPrintHelper()
public FingerPrintHelper(TitaniumIdentityModule module)
{
if (module == null) {
throw new NullPointerException();
}

mModule = module;
final Activity activity = TiApplication.getAppRootOrCurrentActivity();

mBiometricManager = BiometricManager.from(activity);
Expand All @@ -77,12 +83,31 @@ public FingerPrintHelper()

protected boolean isDeviceSupported()
{
if (Build.VERSION.SDK_INT >= 23 && mBiometricManager != null) {
return mBiometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS;
return canUseDeviceBiometrics() || canUseDeviceCredentials();
}

private boolean canUseDeviceBiometrics()
{
if ((Build.VERSION.SDK_INT >= 23) && (mBiometricManager != null)) {
if (mBiometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
return true;
}
}
return false;
}

private boolean canUseDeviceCredentials()
{
if ((Build.VERSION.SDK_INT >= 23) && (mBiometricManager != null)) {
if (mModule.getAuthenticationPolicy() == TitaniumIdentityModule.AUTHENTICATION_POLICY_PASSCODE) {
try {
return mKeyguardManager.isDeviceSecure();
} catch (Exception ex) {
}
}
}
return false;
}
public void stopListening()
{
Iterator cancellationSignalIterator = cancellationSignals.entrySet().iterator();
Expand All @@ -105,33 +130,36 @@ public void stopListening()
@SuppressLint("MissingPermission,NewApi")
public void startListening(KrollFunction callback, KrollObject obj)
{
if (!isDeviceSupported()) {
return;
}

try {
if (initCipher()) {
mCryptoObject = new BiometricPrompt.CryptoObject(mCipher);
} else {
if (canUseDeviceBiometrics()) {
try {
if (initCipher()) {
mCryptoObject = new BiometricPrompt.CryptoObject(mCipher);
} else {
Log.e(TAG, "Unable to initialize cipher");
}
} catch (Exception e) {
Log.e(TAG, "Unable to initialize cipher");
}
} catch (Exception e) {
Log.e(TAG, "Unable to initialize cipher");
}

this.callback = callback;
this.krollObject = obj;
this.callback = callback;
this.krollObject = obj;

mSelfCancelled = false;
mSelfCancelled = false;

final BiometricPrompt.PromptInfo.Builder promptInfo = new BiometricPrompt.PromptInfo.Builder();
promptInfo.setTitle("Scan Fingerprint");
promptInfo.setNegativeButtonText("Cancel");
final BiometricPrompt.PromptInfo.Builder promptInfo = new BiometricPrompt.PromptInfo.Builder();
promptInfo.setTitle("Scan Fingerprint");
promptInfo.setNegativeButtonText("Cancel");

final Executor executor = Executors.newSingleThreadExecutor();
final BiometricPrompt prompt =
new BiometricPrompt((FragmentActivity) TiApplication.getAppCurrentActivity(), executor, this);
prompt.authenticate(promptInfo.build(), mCryptoObject);
final Executor executor = Executors.newSingleThreadExecutor();
final BiometricPrompt prompt =
new BiometricPrompt((FragmentActivity) TiApplication.getAppCurrentActivity(), executor, this);
prompt.authenticate(promptInfo.build(), mCryptoObject);

} else if (canUseDeviceCredentials()) {
this.callback = callback;
this.krollObject = obj;
startDeviceCredentials();
}
}

private void onError(String errMsg)
Expand Down Expand Up @@ -178,7 +206,15 @@ public void onAuthenticationFailed()
@Override
public void onAuthenticationSucceeded(BiometricPrompt.AuthenticationResult result)
{
tryEncrypt();
if (canUseDeviceBiometrics()) {
tryEncrypt();
} else {
if (callback != null && krollObject != null) {
KrollDict dict = new KrollDict();
dict.put("success", true);
callback.callAsync(krollObject, dict);
}
}
}

/**
Expand Down Expand Up @@ -241,7 +277,7 @@ public KrollDict deviceCanAuthenticate(int policy)
// ignore, error gracefully
}

if (!hardwareDetected) {
if (!hardwareDetected && policy != TitaniumIdentityModule.AUTHENTICATION_POLICY_PASSCODE) {
error = error + "Hardware not detected";
}
if (policy == TitaniumIdentityModule.AUTHENTICATION_POLICY_PASSCODE && !hasPasscode) {
Expand Down Expand Up @@ -272,4 +308,22 @@ public KrollDict deviceCanAuthenticate(int policy)
}
return response;
}

private void startDeviceCredentials()
{
KrollDict response = new KrollDict();
response = deviceCanAuthenticate(mModule.getAuthenticationPolicy());
if (response.getBoolean("canAuthenticate")) {
Executor executor = Executors.newSingleThreadExecutor();
BiometricPrompt biometricPrompt =
new BiometricPrompt((FragmentActivity) TiApplication.getAppCurrentActivity(), executor, this);
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle("Enter your device credentials")
.setDeviceCredentialAllowed(true)
.build();
biometricPrompt.authenticate(promptInfo);
} else if (response.containsKey("error")) {
onError(response.getString("error"));
}
}
}
2 changes: 1 addition & 1 deletion android/src/ti/identity/TitaniumIdentityModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ private void init()
{
if (Build.VERSION.SDK_INT >= 23) {
try {
mfingerprintHelper = new FingerPrintHelper();
mfingerprintHelper = new FingerPrintHelper(this);
} catch (Exception e) {
mfingerprintHelper = null;
fingerprintHelperException = e.getCause();
Expand Down
13 changes: 9 additions & 4 deletions apidoc/Identity.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,11 @@ methods:
description: |
A special note for Android:
When you call this method in Android, if you provide an incorrect fingerprint and receive an error
message "Unable to recognize fingerprint", do not call authenticate. Instead, get the user to try
again. If you call authenticate, it will end up in a bad state. This flow will be improved in the
next update for Android.
When you call this method in Android, it will either authenticate the fingerprint or it will fallback
to the device's password, pin or pattern which is the case when biometric means of identification is
not available. If you provide an incorrect fingerprint and receive an error message "Unable to recognize
fingerprint", do not call authenticate. Instead, get the user to try again. If you call authenticate,
it will end up in a bad state. This flow will be improved in the next update for Android.
parameters:
- name: params
summary: Dictionary of arguments passed to the method, e.g. the reason to autheicate and the callback.
Expand Down Expand Up @@ -152,6 +153,10 @@ methods:

- name: isSupported
summary: Determines if the current device supports Touch ID or Face ID.
description: |
This module is only supported on Android 6.0 or newer. So, this method always returns
`false` on older versions of Android.
returns:
type: Boolean

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@titanium-sdk/ti.identity",
"version": "3.0.1",
"version": "3.0.2",
"description": "A collection of API's to authenticate with your device: Keychain/Keystore, Touch ID and Face ID (iOS only)",
"scripts": {
"commit": "git-cz",
Expand Down
1 change: 1 addition & 0 deletions test/unit/specs/keychainitem.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ describe('ti.identity.KeychainItem', () => {
KeychainItem.addEventListener('read', read);
KeychainItem.read();
});

it('reset values and then check if it exists', finish => {
function reset(obj) {
KeychainItem.removeEventListener('reset', reset);
Expand Down
1 change: 1 addition & 0 deletions test/unit/specs/module.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ describe('ti.identity', () => {
it('check value of canAuthenticate after assignement', () => {
var result = Identity.deviceCanAuthenticate();
result.canAuthenticate = true;

expect(result.canAuthenticate).toEqual(true);
});
});
Expand Down

0 comments on commit 22dc20f

Please sign in to comment.