Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[local_auth] Fix device credential only check for API < 30 #6522

Merged
merged 8 commits into from
Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/local_auth/local_auth_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.0.14

* Fixes device credential authentication for API versions before R.

## 1.0.13

* Updates imports for `prefer_relative_imports`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies {
api "androidx.fragment:fragment:1.5.2"
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-inline:4.7.0'
testImplementation 'org.robolectric:robolectric:4.5'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,16 @@ public ArrayList<String> getEnrolledBiometrics() {
}

@VisibleForTesting
public boolean isDeviceSupported() {
public boolean isDeviceSecure() {
if (keyguardManager == null) return false;
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && keyguardManager.isDeviceSecure());
}

@VisibleForTesting
public boolean isDeviceSupported() {
return isDeviceSecure() || canAuthenticateWithBiometrics();
}

private boolean canAuthenticateWithBiometrics() {
if (biometricManager == null) return false;
return biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)
Expand All @@ -270,7 +275,15 @@ private boolean hasBiometricHardware() {
!= BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
}

private boolean canAuthenticateWithDeviceCredential() {
@VisibleForTesting
public boolean canAuthenticateWithDeviceCredential() {
if (Build.VERSION.SDK_INT < 30) {
// Checking for device credential only authentication via the BiometricManager
// is not allowed before API level 30, so we check for presence of PIN, pattern,
// or password instead.
return isDeviceSecure();
}

if (biometricManager == null) return false;
return biometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
== BiometricManager.BIOMETRIC_SUCCESS;
Expand Down Expand Up @@ -334,4 +347,9 @@ final Activity getActivity() {
void setBiometricManager(BiometricManager biometricManager) {
this.biometricManager = biometricManager;
}

@VisibleForTesting
void setKeyguardManager(KeyguardManager keyguardManager) {
this.keyguardManager = keyguardManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.KeyguardManager;
import android.app.NativeActivity;
import android.content.Context;
import androidx.biometric.BiometricManager;
Expand All @@ -34,8 +35,12 @@
import java.util.Collections;
import java.util.HashMap;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

@RunWith(RobolectricTestRunner.class)
public class LocalAuthTest {
@Test
public void authenticate_returnsErrorWhenAuthInProgress() {
Expand Down Expand Up @@ -107,14 +112,13 @@ public void authenticate_properlyConfiguresBiometricOnlyAuthenticationRequest()
}

@Test
@Config(sdk = 30)
public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthenticationRequest() {
final LocalAuthPlugin plugin = spy(new LocalAuthPlugin());
setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class)));
when(plugin.isDeviceSupported()).thenReturn(true);

final BiometricManager mockBiometricManager = mock(BiometricManager.class);
when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
plugin.setBiometricManager(mockBiometricManager);
Expand All @@ -135,6 +139,7 @@ public void authenticate_properlyConfiguresBiometricAndDeviceCredentialAuthentic
}

@Test
@Config(sdk = 30)
public void authenticate_properlyConfiguresDeviceCredentialOnlyAuthenticationRequest() {
final LocalAuthPlugin plugin = spy(new LocalAuthPlugin());
setPluginActivity(plugin, buildMockActivityWithContext(mock(FragmentActivity.class)));
Expand Down Expand Up @@ -343,6 +348,44 @@ public void getEnrolledBiometrics_shouldAddStrongBiometrics() {
});
}

@Test
@Config(sdk = 22)
public void isDeviceSecure_returnsFalseOnBelowApi23() {
final LocalAuthPlugin plugin = new LocalAuthPlugin();
assertFalse(plugin.isDeviceSecure());
}

@Test
@Config(sdk = 23)
public void isDeviceSecure_returnsTrueIfDeviceIsSecure() {
final LocalAuthPlugin plugin = new LocalAuthPlugin();
KeyguardManager mockKeyguardManager = mock(KeyguardManager.class);
plugin.setKeyguardManager(mockKeyguardManager);

when(mockKeyguardManager.isDeviceSecure()).thenReturn(true);
assertTrue(plugin.isDeviceSecure());

when(mockKeyguardManager.isDeviceSecure()).thenReturn(false);
assertFalse(plugin.isDeviceSecure());
}

@Test
@Config(sdk = 30)
public void
canAuthenticateWithDeviceCredential_returnsTrueIfHasBiometricManagerSupportAboveApi30() {
final LocalAuthPlugin plugin = new LocalAuthPlugin();
final BiometricManager mockBiometricManager = mock(BiometricManager.class);
plugin.setBiometricManager(mockBiometricManager);

when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL))
.thenReturn(BiometricManager.BIOMETRIC_SUCCESS);
assertTrue(plugin.canAuthenticateWithDeviceCredential());

when(mockBiometricManager.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL))
.thenReturn(BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED);
assertFalse(plugin.canAuthenticateWithDeviceCredential());
}

private Activity buildMockActivityWithContext(Activity mockActivity) {
final Context mockContext = mock(Context.class);
when(mockActivity.getBaseContext()).thenReturn(mockContext);
Expand Down
2 changes: 1 addition & 1 deletion packages/local_auth/local_auth_android/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: local_auth_android
description: Android implementation of the local_auth plugin.
repository: https://github.com/flutter/plugins/tree/main/packages/local_auth/local_auth_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+local_auth%22
version: 1.0.13
version: 1.0.14

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down