Skip to content

1. fix error.code; 2. Fix the android cancel button; 3. Add the method of authenticateDevice to enable device password #133

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
123 changes: 90 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ It provides a **Default View** that prompts the user to place a finger to the iP
4.0.0 Prefers the new native Android BiometricPrompt lib on any Android >= v23 (M)
4.0.0 also DEPRECATES support for the legacy library that provides support for Samsung & MeiZu phones

3.0.2 and below:
3.0.2 and below:
Using an expandable Android Fingerprint API library, which combines [Samsung](http://developer.samsung.com/galaxy/pass#) and [MeiZu](http://open-wiki.flyme.cn/index.php?title=%E6%8C%87%E7%BA%B9%E8%AF%86%E5%88%ABAPI)'s official Fingerprint API.

Samsung and MeiZu's Fingerprint SDK supports most devices which system versions less than Android 6.0.
Expand Down Expand Up @@ -74,14 +74,14 @@ $ react-native link react-native-fingerprint-scanner
- Add `import com.hieuvp.fingerprint.ReactNativeFingerprintScannerPackage;` to the imports at the top of the file
- Add `new ReactNativeFingerprintScannerPackage()` to the list returned by the `getPackages()` method
2. Append the following lines to `android/settings.gradle`:
```
include ':react-native-fingerprint-scanner'
project(':react-native-fingerprint-scanner').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fingerprint-scanner/android')
```
```
include ':react-native-fingerprint-scanner'
project(':react-native-fingerprint-scanner').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fingerprint-scanner/android')
```
3. Insert the following lines inside the dependencies block in `android/app/build.gradle`:
```
```
implementation project(':react-native-fingerprint-scanner')
```
```

### App Permissions

Expand All @@ -95,13 +95,13 @@ API level 28+ (Uses Android native BiometricPrompt) ([Reference](https://develop
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
```

API level 23-28 (Uses Android native FingerprintCompat) [Reference](https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT))
API level 23-28 (Uses Android native FingerprintCompat) [Reference](https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT))
```xml
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
```

// DEPRECATED in 4.0.0
API level <23 (Uses device-specific native fingerprinting, if available - Samsung & MeiZu only) [Reference](https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT))
API level <23 (Uses device-specific native fingerprinting, if available - Samsung & MeiZu only) [Reference](https://developer.android.com/reference/android/Manifest.permission#USE_FINGERPRINT))
```xml
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
```
Expand Down Expand Up @@ -162,27 +162,62 @@ import FingerprintScanner from 'react-native-fingerprint-scanner';
class FingerprintPopup extends Component {

componentDidMount() {
this._iosTouchID()
}

_iosTouchID = () => {
FingerprintScanner
.authenticate({ description: 'Scan your fingerprint on the device scanner to continue' })
.then(() => {
this.props.handlePopupDismissed();
AlertIOS.alert('Authenticated successfully');
})
.catch((error) => {
this.props.handlePopupDismissed();
switch (error.biometric) {
case 'UserCancel':
AlertIOS.alert('The user clicks the cancel button')
break
case 'AuthenticationFailed':
AlertIOS.alert('User failed to identify 3 times')
break
case 'AuthenticationLockout':
// console.log('Accumulated 5 identification failures, fingerprint identification was locked')
AlertIOS.alert('Identify cumulative multiple failures, temporarily unavailable', [
{
text: 'cancel',
style: 'default',
onPress: () => {
}
}, {
text: 'To unlock',
style: 'default',
onPress: () => {
this._iosAuthenticateDevice()
}
}
])
break
default:
break
}
AlertIOS.alert(error.message);
});
}

_iosAuthenticateDevice = () => {
FingerprintScanner.authenticateDevice().then(() => {
// console.log('Device unlocked')
this._iosTouchID()
}).catch((error) => {
// error.biometric
AlertIOS.alert('catch error:', error.message)
})
}

render() {
return false;
}
}

FingerprintPopup.propTypes = {
handlePopupDismissed: PropTypes.func.isRequired,
};

export default FingerprintPopup;
```

Expand Down Expand Up @@ -222,11 +257,7 @@ class BiometricPopup extends Component {
}

componentDidMount() {
if (this.requiresLegacyAuthentication()) {
this.authLegacy();
} else {
this.authCurrent();
}
this._androidTouchID();
}

componentWillUnmount = () => {
Expand All @@ -237,24 +268,28 @@ class BiometricPopup extends Component {
return Platform.Version < 23;
}

authCurrent() {
_androidTouchID() {
FingerprintScanner.release()
FingerprintScanner
.authenticate({ title: 'Log in with Biometrics' })
.then(() => {
this.props.onAuthenticate();
});
}

authLegacy() {
FingerprintScanner
.authenticate({ onAttempt: this.handleAuthenticationAttemptedLegacy })
.then(() => {
this.props.handlePopupDismissedLegacy();
Alert.alert('Fingerprint Authentication', 'Authenticated successfully');
})
.catch((error) => {
this.setState({ errorMessageLegacy: error.message, biometricLegacy: error.biometric });
this.description.shake();
.catch(error => {
FingerprintScanner.release()
switch (error.biometric) {
case 'UserCancel':
AlertIOS.alert('Click the cancel button')
break
case 'DeviceLocked':
AlertIOS.alert('Accumulated 5 identification failures, fingerprint identification was locked')
break
case 'DeviceLockedPermanent':
AlertIOS.alert('Accumulates many times to recognize the failure, is locked permanently, needs to unlock')
break
default:
break
}
});
}

Expand Down Expand Up @@ -339,6 +374,24 @@ componentDidMount() {
}
```

### `authenticateDevice()`: (iOS)
Unlock with the device password.

- Returns a `Promise<string>`
- `error: FingerprintScannerError { name, message, biometric }` - The name and message of failure and the biometric type in use.


```javascript
FingerprintScanner
.authenticateDevice()
.then(() => {
// AlertIOS.alert('Device unlocked')
this._iosTouchID()
}).catch((error) => {
// AlertIOS.alert('catch error:', error.message, error.biometric)
})
```

### `authenticate({ description, fallbackEnabled })`: (iOS)
Starts Fingerprint authentication on iOS.

Expand Down Expand Up @@ -438,6 +491,7 @@ componentWillUnmount() {

| Name | Message |
|---|---|
| AuthenticationLockout | Authentication lockout |
| AuthenticationNotMatch | No match |
| AuthenticationFailed | Authentication was not successful because the user failed to provide valid credentials |
| AuthenticationTimeout | Authentication was not successful because the operation timed out |
Expand All @@ -450,6 +504,7 @@ componentWillUnmount() {
| DeviceLockedPermanent | Authentication was not successful, device must be unlocked via password |
| DeviceOutOfMemory | Authentication could not proceed because there is not enough free memory on the device |
| HardwareError | A hardware error occurred |
| UserDeviceCancel | Authentication Device was canceled |
| FingerprintScannerUnknownError | Could not authenticate for an unknown reason |
| FingerprintScannerNotSupported | Device does not support Fingerprint Scanner |
| FingerprintScannerNotEnrolled | Authentication could not start because Fingerprint Scanner has no enrolled fingers |
Expand All @@ -458,3 +513,5 @@ componentWillUnmount() {
## License

MIT


Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public AuthCallback(final Promise promise) {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
super.onAuthenticationError(errorCode, errString);
this.promise.reject(biometricPromptErrName(errorCode), TYPE_BIOMETRICS);
this.promise.reject(biometricPromptErrName(errorCode), biometricPromptErrName(errorCode));
}

@Override
Expand Down Expand Up @@ -193,12 +193,10 @@ private String getSensorError() {
public void authenticate(String title, String subtitle, String description, String cancelButton, final Promise promise) {
if (requiresLegacyAuthentication()) {
legacyAuthenticate(promise);
}
else {
} else {
final String errorName = getSensorError();
if (errorName != null) {
promise.reject(errorName, TYPE_BIOMETRICS);
ReactNativeFingerprintScannerModule.this.release();
promise.reject(errorName, errorName);
return;
}

Expand Down Expand Up @@ -236,13 +234,12 @@ public void isSensorAvailable(final Promise promise) {
// current API
String errorName = getSensorError();
if (errorName != null) {
promise.reject(errorName, TYPE_BIOMETRICS);
promise.reject(errorName, errorName);
} else {
promise.resolve(TYPE_BIOMETRICS);
}
}


// for Samsung/MeiZu compat, Android v16-23
private FingerprintIdentify getFingerprintIdentify() {
if (mFingerprintIdentify != null) {
Expand Down Expand Up @@ -305,12 +302,11 @@ public void onNotMatch(int availableTimes) {

@Override
public void onFailed(boolean isDeviceLocked) {
if(isDeviceLocked){
if (isDeviceLocked) {
promise.reject("AuthenticationFailed", "DeviceLocked");
} else {
promise.reject("AuthenticationFailed", TYPE_FINGERPRINT_LEGACY);
}
ReactNativeFingerprintScannerModule.this.release();
}

@Override
Expand Down
32 changes: 29 additions & 3 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export type AuthenticateAndroid = {
export type Biometrics = 'Touch ID' | 'Face ID' | 'Biometrics';

export type Errors =
| { name: 'AuthenticationNotMatch'; message: 'No match' }
| { name: 'AuthenticationLockout'; message: 'Authentication lockout'; }
| { name: 'AuthenticationNotMatch'; message: 'No match'; }
| {
name: 'AuthenticationFailed';
message: 'Authentication was not successful because the user failed to provide valid credentials';
Expand Down Expand Up @@ -44,11 +45,11 @@ export type Errors =
}
| {
name: 'FingerprintScannerNotAvailable';
message: ' Authentication could not start because Fingerprint Scanner is not available on the device';
message: ' Authentication could not start because Fingerprint Scanner is not available on the device';
}
| {
name: 'FingerprintScannerNotEnrolled';
message: ' Authentication could not start because Fingerprint Scanner has no enrolled fingers';
message: ' Authentication could not start because Fingerprint Scanner has no enrolled fingers';
}
| {
name: 'FingerprintScannerUnknownError';
Expand All @@ -73,6 +74,10 @@ export type Errors =
| {
name: 'HardwareError';
message: 'A hardware error occurred.';
}
| {
name: 'UserDeviceCancel';
message: 'Authentication Device was canceled';
};

export type FingerprintScannerError = { biometric: Biometrics } & Errors;
Expand Down Expand Up @@ -166,6 +171,27 @@ export interface FingerPrintProps {
authenticate: (
platformProps: AuthenticateIOS | AuthenticateAndroid
) => Promise<void>;

/**
### authenticateDevice(): (iOS)
Unlock with the device password.
- Returns a `Promise<Biometrics>`
- `error: FingerprintScannerError { name, message, biometric }` - The name and message of failure and the biometric type in use.

-------------
Exemple

```
FingerprintScanner
.authenticateDevice()
.then(() => {
AlertIOS.alert('Authenticated successfully');
})
.catch(error => this.setState({ errorMessage: error.message }));
```
------------
*/
authenticateDevice: () => Promise<Biometrics>;
}

declare const FingerprintScanner: FingerPrintProps;
Expand Down
Loading