GithubHelp home page GithubHelp logo

aparajita / capacitor-biometric-auth Goto Github PK

View Code? Open in Web Editor NEW
111.0 5.0 17.0 550 KB

Easy access to native biometric auth APIs on iOS and Android

License: MIT License

Ruby 1.50% Java 26.01% Objective-C 0.92% Swift 7.16% JavaScript 2.87% TypeScript 34.11% Shell 0.31% CSS 27.12%
capacitor capacitor-plugin capacitorjs biometry biometric-authentication capacitor-plugins capacitor-android capacitor-ios

capacitor-biometric-auth's Introduction

capacitor-biometric-auth  npm version

This plugin for Capacitor 6 provides access to native biometry and device credentials on iOS and Android. It supports every type of biometry and every configuration option on both platforms. In addition, biometry and device credentials are simulated on the web so you can test your logic without making any changes to your code.

🛑 BREAKING CHANGES:

  • If you are upgrading from a version prior to 6.0.0, please note that authenticate() now throws an instance of BiometryError, and BiometryError.code is now typed as BiometryErrorType.

  • If you are upgrading from a version prior to 7.0.0, please note that authenticate() will immediately present a prompt for device credentials if deviceIsSecure is true, allowDeviceCredentials is true, and no biometry of the requested strength is available.

  • If you are upgrading from a version prior to 8.0.0, please note that this plugin now requires Capacitor 6+.

  • If you are upgrading from a version prior to 8.0.0, please note that addResumeListener now always returns a Promise and must be awaited.

Installation

pnpm add @aparajita/capacitor-biometric-auth
npm install @aparajita/capacitor-biometric-auth
yarn add @aparajita/capacitor-biometric-auth

Not using pnpm? You owe it to yourself to give it a try. It’s faster, better with monorepos, and uses way, way less disk space than the alternatives.

iOS

👉 IMPORTANT!! In order to use Face ID, you must add the NSFaceIDUsageDescription key to your Info.plist file. This is a string that describes why your app needs access to Face ID. If you don’t add this key, the system won’t allow your app to use Face ID.

  1. In Xcode, open your app’s Info.plist file.
  2. Hover your mouse over one of the existing keys, and click the + button that appears.
  3. In the popup that appears, type Privacy - Face ID Usage Description and press Enter.
  4. In the Value column, enter a string that describes why your app needs access to Face ID.
  5. Save your changes.

Usage

The API is extensively documented in the TypeScript definitions file. There is also (somewhat incomplete auto-generated) documentation below. For a complete example of how to use this plugin in practice, see the demo app.

Checking availability

Although not strictly necessary, before giving the user the option to use biometry (such as displaying a biometry icon), you will probably want to call checkBiometry() and inspect the CheckBiometryResult to see what (if any) biometry and/or device credentials are available on the device. Note the following:

  • isAvailable may be false but biometryType may indicate the presence of biometry on the device. This occurs if the current user is not enrolled in biometry, or if biometry has been disabled for the current app. In such cases the reason and code will tell you why.

  • On iOS, isAvailable and strongBiometryIsAvailable will always have the same value. On Android, isAvailable will be true if any type of biometry is available, but strongBiometryIsAvailable will be true only if strong biometry is available. For example, on a typical device, if the device supports both fingerprint and face authentication, isAvailable will be true if either is enrolled, but strongBiometryIsAvailable will be true only if fingerprint authentication is enrolled.

  • biometryTypes may contain more than one type of biometry. This occurs on Android devices that support multiple types of biometry. In such cases biometryType will indicate the primary (most secure) type of biometry, and the biometryTypes array will contain all of the biometry types supported by the device. Note that Android only guarantees that one of the types is actually available.

Because the availability of biometry can change while your app is in the background, it’s important to check availability when your app resumes. By calling addResumeListener() you can register a callback that is passed a CheckBiometryResult when your app resumes.

Example

import { CheckBiometryResult } from './definitions'

let appListener: PluginListenerHandle

function updateBiometryInfo(info: CheckBiometryResult): void {
  if (info.isAvailable) {
    // Biometry is available, info.biometryType will tell you the primary type.
  } else {
    // Biometry is not available, info.reason and info.code will tell you why.
  }
}

async function onComponentMounted(): Promise<void> {
  updateBiometryInfo(await BiometricAuth.checkBiometry())

  try {
    appListener = await BiometricAuth.addResumeListener(updateBiometryInfo)
  } catch (error) {
    if (error instanceof Error) {
      console.error(error.message)
    }
  }
}

async function onComponentUnmounted(): Promise<void> {
  await appListener?.remove()
}

Authenticating

To initiate biometric authentication call authenticate(). authenticate takes an AuthenticateOptions object which you will want to use in order to control the behavior and appearance of the biometric prompt.

If authentication succeeds, the Promise resolves. If authentication fails, the Promise is rejected with an instance of BiometryError, which has two properties:

Property Type Description
message string A description of the error suitable for debugging
code BiometryErrorType What caused the error

Example

import {
  AndroidBiometryStrength,
  BiometryError,
  BiometryErrorType,
} from './definitions'

async function authenticate(): Promise<void> {
  try {
    await BiometricAuth.authenticate({
      reason: 'Please authenticate',
      cancelTitle: 'Cancel',
      allowDeviceCredential: true,
      iosFallbackTitle: 'Use passcode',
      androidTitle: 'Biometric login',
      androidSubtitle: 'Log in using biometric authentication',
      androidConfirmationRequired: false,
      androidBiometryStrength: AndroidBiometryStrength.weak,
    })
  } catch (error) {
    // error is always an instance of BiometryError.
    if (error instanceof BiometryError) {
      if (error.code !== BiometryErrorType.userCancel) {
        // Display the error.
        await showAlert(error.message)
      }
    }
  }
}

Biometry support

web

On the web, you can fake any of the supported biometry types by calling setBiometryType().

iOS

On iOS, Touch ID and Face ID are supported.

Android

On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will initially present the primary (most secure) available type, which is determined by the system.

API

This is the public interface of the plugin.

checkBiometry()

checkBiometry() => Promise<CheckBiometryResult>

Check to see what biometry type (if any) is available.

Returns: Promise<CheckBiometryResult>


setBiometryType(...)

setBiometryType(type: BiometryType | string | Array<BiometryType | string> | undefined) => Promise<void>

web only

On the web, this method allows you to dynamically simulate different types of biometry. You may either pass BiometryType enums or the string names of the BiometryType enums. If undefined or a string is passed and it isn't a valid value, nothing happens.

Param Type
type string | BiometryType | (string | BiometryType)[]

setBiometryIsEnrolled(...)

setBiometryIsEnrolled(isSecure: boolean) => Promise<void>

web only

On the web, this method allows you to dynamically simulate whether or not the user has enrolled in biometry.

Param Type
isSecure boolean

setDeviceIsSecure(...)

setDeviceIsSecure(isSecure: boolean) => Promise<void>

web only

On the web, this method allows you to dynamically simulate whether or not the user has secured the device with a PIN, pattern or passcode.

Param Type
isSecure boolean

authenticate(...)

authenticate(options?: AuthenticateOptions) => Promise<void>

Prompt the user for authentication. If authorization fails for any reason, the promise is rejected with a BiometryError.

For detailed information about the behavior on iOS, see:

https://developer.apple.com/documentation/localauthentication/lapolicy/deviceownerauthenticationwithbiometrics

Some versions of Android impose a limit on the number of failed attempts. If allowDeviceCredential is true, when the limit is reached the user will then be presented with a device credential prompt. If allowDeviceCredential is false, when the limit is reached authenticate() will reject with a BiometryErrorType of biometryLockout, after which the user will have to wait the system-defined length of time before being allowed to authenticate again.

Param Type
options AuthenticateOptions

addResumeListener(...)

addResumeListener(listener: ResumeListener) => Promise<PluginListenerHandle>

Register a function that will be called when the app resumes. The function will be passed the result of checkBiometry().

👉 NOTE: checkBiometry() must be called at least once before calling this method.

Param Type
listener ResumeListener

Returns: Promise<PluginListenerHandle>


Interfaces

CheckBiometryResult

Prop Type Description
isAvailable boolean True if the device supports at least weak biometric authentication and the current user has enrolled in some form of biometry. Note that if strongBiometryIsAvailable is true, this will also be true.
strongBiometryIsAvailable boolean True if the device has strong biometric authentication capability and the current user has enrolled in that strong biometry.

On iOS this value and isAvailable will always be the same, since iOS only supports strong biometry.

On Android, for example, if the device supports both fingerprint and face authentication, and the user has enrolled only in face authentication, and Android considers face authentication on that device to be weak, then isAvailable will be true but this value will be false.
biometryType BiometryType The primary (most secure) type of biometry supported by the device. Note that supported is not the same as available, which requires the biometry to be enrolled.
biometryTypes BiometryType[] All of the biometry types supported by the hardware on the device (currently only Android devices support multiple biometry types). If no biometry is supported, i.e. biometryType === <a href="#biometrytype">BiometryType.none, this will be an empty array.

Note that supported is not the same as available, which requires the biometry to be enrolled.
deviceIsSecure boolean Returns true if the device is secure. On iOS, this means that the device has a passcode set. On Android, this means that the device has a PIN, pattern, or password set.
reason string If weak or better biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string.
code BiometryErrorType If weak or better biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the BiometryErrorType enum values, and is consistent across platforms.
strongReason string If strong biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string.

On iOS, this will always be the same as reason, since all biometry on iOS is strong.
strongCode BiometryErrorType If strong biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the BiometryErrorType enum values, and is consistent across platforms.

On iOS, this will always be the same as code, since all biometry on iOS is strong.

Array

Prop Type Description
length number Gets or sets the length of the array. This is a number one higher than the highest index in the array.
Method Signature Description
toString () => string Returns a string representation of an array.
toLocaleString () => string Returns a string representation of an array. The elements are converted to string using their toLocaleString methods.
pop () => T | undefined Removes the last element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
push (...items: T[]) => number Appends new elements to the end of an array, and returns the new length of the array.
concat (...items: ConcatArray<T>[]) => T[] Combines two or more arrays.
This method returns a new array without modifying any existing arrays.
concat (...items: (T | ConcatArray<T>)[]) => T[] Combines two or more arrays.
This method returns a new array without modifying any existing arrays.
join (separator?: string) => string Adds all the elements of an array into a string, separated by the specified separator string.
reverse () => T[] Reverses the elements in an array in place.
This method mutates the array and returns a reference to the same array.
shift () => T | undefined Removes the first element from an array and returns it.
If the array is empty, undefined is returned and the array is not modified.
slice (start?: number, end?: number) => T[] Returns a copy of a section of an array.

For both start and end, a negative index can be used to indicate an offset from the end of the array. For example, -2 refers to the second to last element of the array. | | sort | (compareFn?: ((a: T, b: T) => number) | undefined) => this | Sorts an array in place. This method mutates the array and returns a reference to the same array. | | splice | (start: number, deleteCount?: number) => T[] | Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. | | splice | (start: number, deleteCount: number, ...items: T[]) => T[] | Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. | | unshift | (...items: T[]) => number | Inserts new elements at the start of an array, and returns the new length of the array. | | indexOf | (searchElement: T, fromIndex?: number) => number | Returns the index of the first occurrence of a value in an array, or -1 if it is not present. | | lastIndexOf | (searchElement: T, fromIndex?: number) => number | Returns the index of the last occurrence of a specified value in an array, or -1 if it is not present. | | every | <S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any) => this is S[] | Determines whether all the members of an array satisfy the specified test. | | every | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => boolean | Determines whether all the members of an array satisfy the specified test. | | some | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => boolean | Determines whether the specified callback function returns true for any element of an array. | | forEach | (callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void | Performs the specified action for each element in an array. | | map | <U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[] | Calls a defined callback function on each element of an array, and returns an array that contains the results. | | filter | <S extends T>(predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any) => S[] | Returns the elements of an array that meet the condition specified in a callback function. | | filter | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => T[] | Returns the elements of an array that meet the condition specified in a callback function. | | reduce | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T) => T | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | reduce | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T) => T | | | reduce | <U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U) => U | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | reduceRight | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T) => T | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | reduceRight | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T) => T | | | reduceRight | <U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U) => U | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. |

ConcatArray

Prop Type
length number
Method Signature
join (separator?: string) => string
slice (start?: number, end?: number) => T[]

AuthenticateOptions

Prop Type Description
reason string Displays the reason for requesting authentication in the authentication dialog presented to the user.

Default: System default
cancelTitle string iOS: The system presents a cancel button during biometric authentication to let the user abort the authentication attempt. The button appears every time the system asks the user to present a finger registered with Touch ID. For Face ID, the button only appears if authentication fails and the user is prompted to try again. Either way, the user can stop trying to authenticate by tapping the button.

Android: The text for the negative button. This would typically be used as a "Cancel" button, but may be also used to show an alternative method for authentication, such as a screen that asks for a backup password.

Default: "Cancel"
allowDeviceCredential boolean If true, allows for authentication using device unlock credentials.

Default: false.

iOS: If biometry is available, enrolled, and not disabled, the system uses that first. After the first Touch ID failure or second Face ID failure, if iosFallbackTitle is not an empty string, a fallback button appears in the authentication dialog. If the user taps the fallback button, the system prompts the user for the device passcode or the user’s password. If iosFallbackTitle is an empty string, no fallback button will appear.

If no biometry is enrolled and enabled, and a passcode is set, the system immediately prompts the user for the device passcode or user’s password. Authentication fails with the error code passcodeNotSet if the device passcode isn’t enabled.

If a passcode is not set on the device, a passcodeNotSet error is returned.

The system disables passcode authentication after 6 unsuccessful attempts, with progressively increasing delays between attempts.

The title of the fallback button may be customized by setting iosFallbackTitle to a non-empty string.

Android: The user will first be prompted to authenticate with biometrics, but also given the option to authenticate with their device PIN, pattern, or password by tapping a button in the authentication dialog. The title of the button is supplied by the system.
iosFallbackTitle string The system presents a fallback button when biometric authentication fails — for example, because the system doesn’t recognize the presented finger, or after several failed attempts to recognize the user’s face.

If allowDeviceCredential is false, tapping this button dismisses the authentication dialog and returns the error code userFallback. If undefined, the localized system default title is used. Passing an empty string hides the fallback button completely.

If allowDeviceCredential is true and this is undefined, the localized system default title is used.
androidTitle string Title for the Android dialog. If not supplied, the system default is used.
androidSubtitle string Subtitle for the Android dialog. If not supplied, the system default is used.
androidConfirmationRequired boolean Determines if successful weak biometric authentication must be confirmed.

For information on this setting, see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean).

Default: true
androidBiometryStrength AndroidBiometryStrength Set the strength of Android biometric authentication that will be accepted.

👉 NOTE: On Android 9 & 10 (API 28-29), this will effectively always be .weak if allowDeviceCredential is true. This is a known limitation of the Android API. 🤯

Default: AndroidBiometryStrength.weak

PluginListenerHandle

Method Signature
remove () => Promise<void>

Type Aliases

ResumeListener

The signature of the callback passed to addResumeListener().

(info: CheckBiometryResult): void

Enums

BiometryType

Members Description
none
touchId iOS Touch ID
faceId iOS Face ID
fingerprintAuthentication Android fingerprint authentication
faceAuthentication Android face authentication
irisAuthentication Android iris authentication

BiometryErrorType

Members Value
none ''
appCancel 'appCancel'
authenticationFailed 'authenticationFailed'
invalidContext 'invalidContext'
notInteractive 'notInteractive'
passcodeNotSet 'passcodeNotSet'
systemCancel 'systemCancel'
userCancel 'userCancel'
userFallback 'userFallback'
biometryLockout 'biometryLockout'
biometryNotAvailable 'biometryNotAvailable'
biometryNotEnrolled 'biometryNotEnrolled'
noDeviceCredential 'noDeviceCredential'

AndroidBiometryStrength

Members Description
weak authenticate() will present any available biometry.
strong authenticate() will only present strong biometry.

capacitor-biometric-auth's People

Contributors

a1jan avatar aparajita avatar sunagaga avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

capacitor-biometric-auth's Issues

Ask for permission without calling 'authenticate'

Hi, I believe there is no way to do this currently, so this is a feature request. We have a settings page in our app where we ask users whether they want to use biometrics or not. If they enable, we call authenticate on app login and FaceId permission is asked here. It would be good to have a way to ask for FaceId permission in settings rather than the login page when they actually attempt to use it for the first time. I believe this would improve the UX. Thanks in advance 🙏

Question: Does `biometryType` really indicate the primary (most secure) type of biometry supported?

Hello @aparajita 👋

Thanks for creating and maintaining this plugin!

Currently I'm checking what's the best option to implement biometric auth for my Capacitor app and if for iOS everything is clear, then for Android it's not that obvious. Meaning, there could be more than one type availbale and how to handle that properly is the question...

In the doc mentioned that CheckBiometryResult biometryType returns the primary (most secure) type of biometry supported by the device, however in the code I see that first element from the collection is taken:

biometryTypes = getDeviceBiometryTypes();
result.put("biometryType", biometryTypes.get(0).getType());

And if both FEATURE_FINGERPRINT and FEATURE_FACE are supported, then FEATURE_FINGERPRINT will be returned. @aparajita, is that intentional? 🤔

Capacitor 5 Support

hello - Thanks for plugin, any approximate time line when it will be updated to Capacitor 5

error instanceof BiometryError, error.code typing

For developer experience with typescript, I want to do error instanceof BiometryError like the following:

    try {
      await BiometricAuth.authenticate({
        allowDeviceCredential: true,
      });
    } catch (error) {
      if (error instanceof BiometryError && error.code === 'biometryNotEnrolled') {
         // do thing with error.code
      }

      throw error;
    }

Because error instanceof BiometryError evaluates to false, I have to do the following:

    try {
      await BiometricAuth.authenticate({
        allowDeviceCredential: true,
      });
    } catch (error) {
      if (error && typeof error === 'object' &&'code' in error && error.code === 'biometryNotEnrolled') {
        
      }
      throw error;
    }

or I have to unsafely cast.

Also, BiometryError.code is typed as string, it would be nice if it was BiometryErrorType.

Thank you!

OAuth linking?

Forgive me if this is a dumb question but how do I link biometric authentication with OAuth? For example, I am using firebase and allow users to sign in via username/password, google, etc. If a user has never logged in via username/password (which is the credential I would expect biometric auth to give me from the keychain?), how do I link their biometric auth data to their OAuth provider to show them as logged in? Any info here would be appreciated!

iOS Touch ID dialog displays when calling checkBiometry()

iOS Touch ID dialog displays when checkBiometry() is called.
This doesn't seem like the expected behaviour - was just expecting the metadata to return.
I thought we called authenticate() to actually perform a touch ID or face ID on the device?

Background Issue

Hi there,

I'm not sure if it's related to this plugin but I got this gray background each time the biometric is fired.

issue

Have you already encounter this kind of issue ?
Regards

Add namespace in build.gradle to fix android compilation issue on Capacitor 5.x.x

Hi! 👋

Firstly, thanks for your work on this project! 🙂

The aim of this PR is to patch @aparajita/[email protected] for the project I'm working on by adding the namespace to the android build.gradle file for the plugin to be compiled for android using capacitor 5.x.x

Here is the diff that solved my problem:

diff --git a/node_modules/@aparajita/capacitor-biometric-auth/android/build.gradle b/node_modules/@aparajita/capacitor-biometric-auth/android/build.gradle
index 4d3a551..282211d 100644
--- a/node_modules/@aparajita/capacitor-biometric-auth/android/build.gradle
+++ b/node_modules/@aparajita/capacitor-biometric-auth/android/build.gradle
@@ -17,6 +17,7 @@ buildscript {
 apply plugin: 'com.android.library'
 
 android {
+    namespace 'com.aparajita.capacitor.biometricauth'
     compileSdkVersion project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 32
     defaultConfig {
         minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22

Crashes on iOS 16.4

My code is pretty much the same as the demo code. Crashes on iOS 16.4 simulator.

AAPT: error: resource style/AppTheme not found

When I run the command ./gradlew assembleRelease in the Android Studio terminal, I get the following error:

Execution failed for task ':aparajita-capacitor-biometric-auth:verifyReleaseResources'.
> A failure occurred while executing com.android.build.gradle.tasks.VerifyLibraryResourcesTask$Action
   > Android resource linking failed
     ERROR:AAPT: error: resource style/AppTheme (aka com.aparajita.capacitor.biometricauth:style/AppTheme) not found.
     error: failed linking references.

Can you confirm this, or is this an issue on my side?

  • when I do a debug build, everything works fine
  • my apps styles.xml contains a theme named 'AppTheme'

Trouble running pnpm build -- Unexpected token ! in JSON at position 0

Hello again! At your suggestion here, I'm taking a crack at making the modifications discussed in #21. I admit to not having much experience with pnpm or npm. After getting pnpm installed, cloning my unmodified fork, then running pnpm install, finally running pnpm build, I receive the following output, ending with what looks like a syntax exception thrown.


> @aparajita/[email protected] prebuild /Users/me/Desktop/capacitor-biometric-auth
> pnpm lint


> @aparajita/[email protected] lint /Users/me/Desktop/capacitor-biometric-auth
> pnpm lint.eslint . && pnpm lint.prettier '**/*.{js,mjs,ts,json,md,java}' && pnpm lint.tsc && swiftly ios


> @aparajita/[email protected] lint.eslint /Users/me/Desktop/capacitor-biometric-auth
> eslint --fix --cache --ext .js,.cjs,.mjs,.ts --max-warnings 0 "."


> @aparajita/[email protected] lint.prettier /Users/me/Desktop/capacitor-biometric-auth
> prettier --write --cache --list-different "**/*.{js,mjs,ts,json,md,java}"


> @aparajita/[email protected] lint.tsc /Users/me/Desktop/capacitor-biometric-auth
> tsc --noEmit

Unexpected token ! in JSON at position 0
 ELIFECYCLE  Command failed with exit code 1.
 ELIFECYCLE  Command failed with exit code 1.

I wish I knew what JSON it was referring to. Does this seem like an issue on my side? If so, any suggestions to resolve it?

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.util.ArrayList.get(int)' on a null object reference

Hi

First of all, thank you for developing this plugin!

We have issues on Android and IOS when we call the authenticate-method. Getting a NullPointerException.
FATAL EXCEPTION: CapacitorPlugins Process: ***.***.app, PID: 17832 java.lang.RuntimeException: java.lang.reflect.InvocationTargetException at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:789) at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA(Unknown Source:0) at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(Unknown Source:8) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.os.HandlerThread.run(HandlerThread.java:67) Caused by: java.lang.reflect.InvocationTargetException at java.lang.reflect.Method.invoke(Native Method) at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138) at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:780) at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA(Unknown Source:0)  at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(Unknown Source:8)  at android.os.Handler.handleCallback(Handler.java:942)  at android.os.Handler.dispatchMessage(Handler.java:99)  at android.os.Looper.loopOnce(Looper.java:201)  at android.os.Looper.loop(Looper.java:288)  at android.os.HandlerThread.run(HandlerThread.java:67)  Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.util.ArrayList.get(int)' on a null object reference at com.aparajita.capacitor.biometricauth.BiometricAuthNative.authenticate(BiometricAuthNative.java:198) at java.lang.reflect.Method.invoke(Native Method)  at com.getcapacitor.PluginHandle.invoke(PluginHandle.java:138)  at com.getcapacitor.Bridge.lambda$callPluginMethod$0(Bridge.java:780)  at com.getcapacitor.Bridge.$r8$lambda$ehFTi5f4HhVNFKTbCKAYDkpQYRA(Unknown Source:0)  at com.getcapacitor.Bridge$$ExternalSyntheticLambda3.run(Unknown Source:8)  at android.os.Handler.handleCallback(Handler.java:942)  at android.os.Handler.dispatchMessage(Handler.java:99)  at android.os.Looper.loopOnce(Looper.java:201)  at android.os.Looper.loop(Looper.java:288)  at android.os.HandlerThread.run(HandlerThread.java:67) 

Any ideas?

Thanks!

Fallback to PIN on Android

I'm trying to implement this plugin for Android, and it works great when I do have any biometric types on device. But I'd like it to fallback to the PIN input if not. Is this possible?
This is are the options I'm passing to the authenticate method right now:

{
   "reason":"Please authenticate",
   "cancelTitle":"Cancel",
   "allowDeviceCredential":true,
   "iosFallbackTitle":"Use passcode",
   "androidTitle":"Biometric login",
   "androidSubtitle":"Log in using biometric authentication",
   "androidConfirmationRequired":true
}

And it's returning this error:

{
   "save":false,
   "callbackId":"51279928",
   "pluginId":"BiometricAuthNative",
   "methodName":"internalAuthenticate",
   "success":false,
   "error": {
      "message":"There is no biometric hardware on this device.",
      "code":"biometryNotAvailable"
   }
}

implementing in capacitor with angular

hi, is there a tutorial on how i can implemented this plugin in angular? from the demo version that you provided, it's using vue.

somehow i try to implemented it in my apps and build it to iOS, but it got this error :
⚡️ WebView loaded
⚡️ [log] - begin init biometric..
⚡️ [error] - Error during biometric auth: {"code":"UNIMPLEMENTED"}

here's my code snippet :

import { registerPlugin } from '@capacitor/core';
import { BiometricAuthPlugin } from '@aparajita/capacitor-biometric-auth';
const BiometricAuth = registerPlugin<BiometricAuthPlugin>('BiometricAuth');

  initBiometric() {
    console.log('begin init biometric..');
    BiometricAuth.checkBiometry()
      .then((result: any) => {
        console.log('auth biometry', result);
      })
      .catch((error: any) => {
        // Handle any errors
        console.error('Error during biometric check:', error);
      });
  }

  ngOnInit() {
    this.initBiometric();
  }

Thanks!

Question: Customize Biometric Input Prompt Screen

Hello, thank you for making this plugin!

I have pretty much everything set up and working but was wondering if it's possible to customize the screen where you input your passcode?

This is a fallback scenario on iOS where a user opts into using biometrics but has disabled Face ID/Fingerprints.

iosFallbackTitle: 'Use passcode',

image

Any help would be appreciated, even if we need to write some code outside of ionic, we're not totally sure where this comes from or if we can customize the page? It would be nice to be able to change the background color, add a logo and default to the numeric keyboard, etc, etc.

Thanks!

Cannot access 'registerPlugin' before initialization

When try to build with android studio I received in logcat this error so app is broken.
I think is related to rollup (with vite) but the rollup.config.mjs seems ok.

My configuration is (vite.config.ts):

import legacy from '@vitejs/plugin-legacy'
import react from '@vitejs/plugin-react'
import inject from '@rollup/plugin-inject'
import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import rollupNodePolyFill from 'rollup-plugin-polyfill-node'
import { defineConfig } from 'vite'

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      mqtt: 'mqtt/dist/mqtt.js',
    },
  },
  plugins: [
    react(),
    legacy()
  ],
  optimizeDeps: {
    esbuildOptions: {
      define: {
        global: 'globalThis'
      },
      plugins: [
        NodeGlobalsPolyfillPlugin({
          buffer: true
        })
      ]
    }
  },
  build: {
    minify: false,
    rollupOptions: {
      plugins: [
        inject({ Buffer: ['buffer', 'Buffer'] }),
        rollupNodePolyFill(),
      ],
      output:{
        manualChunks(id) {
          if (id.includes('node_modules')) {
            return id.toString().split('node_modules/')[1].split('/')[0].toString();
          }
        }
      }
    }
  },
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/setupTests.ts',
  }
})

My ionic info:

$ ionic info

Ionic:

   Ionic CLI       : 7.1.1 (C:\Users\LAG\AppData\Roaming\nvm\v16.20.0\node_modules\@ionic\cli)
   Ionic Framework : @ionic/react 7.3.1

Capacitor:

   Capacitor CLI      : 5.3.0
   @capacitor/android : 5.3.0
   @capacitor/core    : 5.3.0
   @capacitor/ios     : 5.3.0

Utility:

   cordova-res : 0.15.4
   native-run  : 1.7.2

System:

   NodeJS : v16.20.0 (C:\Program Files\nodejs\node.exe)
   npm    : 8.19.4
   OS     : Windows 10

My package.json relevant is:

  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview",
    "test.e2e": "cypress run",
    "test.unit": "vitest",
    "lint": "eslint",
    "ionic:build": "npm run build",
    "ionic:serve": "vite dev --host",
    "ionic:buildDEV": "tsc && vite build --mode development"
  },
  "dependencies": {
    "@aparajita/capacitor-biometric-auth": "^5.0.2",
    "@capacitor/android": "5.3.0",
    "@capacitor/app": "5.0.6",
    "@capacitor/core": "5.3.0",
    "@capacitor/device": "^5.0.6",
    "@capacitor/haptics": "5.0.6",
    "@capacitor/ios": "5.3.0",
    "@capacitor/keyboard": "5.0.6",
    "@capacitor/status-bar": "5.0.6",
    "@emotion/core": "^11.0.0",
    "@emotion/styled": "^11.11.0",
    "@ionic/react": "^7.3.1",
    "@ionic/react-router": "^7.3.1",
    "@tarragon/swipeable-tabs": "^0.1.11",
    "@types/react-router": "^5.1.20",
    "@types/react-router-dom": "^5.3.3",
    "antd": "^5.8.4",
    "axios": "^1.4.0",
    "flag-icons": "^6.10.0",
    "ionicons": "^7.1.2",
    "lodash": "^4.17.21",
    "mqtt": "^5.0.3",
    "onesignal-cordova-plugin": "^3.3.1",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-intl-universal": "^2.6.21",
    "react-router": "^5.3.4",
    "react-router-dom": "^5.3.4",
    "styled-components": "^6.0.7",
    "swiper": "^10.2.0",
    "use-force-update": "^1.0.11",
    "zustand": "^4.4.1"
  },
  "devDependencies": {
    "@capacitor/cli": "5.3.0",
    "@esbuild-plugins/node-globals-polyfill": "^0.2.3",
    "@testing-library/jest-dom": "^6.0.1",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.4.3",
    "@types/jest": "^29.5.4",
    "@types/lodash": "^4.14.197",
    "@types/react": "^18.2.21",
    "@types/react-dom": "^18.2.7",
    "@vitejs/plugin-legacy": "^4.1.1",
    "@vitejs/plugin-react": "^4.0.4",
    "cypress": "^12.17.4",
    "eslint": "^8.47.0",
    "eslint-plugin-react": "^7.33.2",
    "jsdom": "^22.1.0",
    "rollup-plugin-polyfill-node": "^0.12.0",
    "typescript": "^5.1.6",
    "vite": "^4.4.9",
    "vitest": "^0.34.2"
  },

I set minified: false because instead error was fuorviant (Cannot access 'registerPlugin' before initialization)

Any suggest for solve or workaround this?

NOTE: Your Android app must use a base theme named "AppTheme"

At the following location https://github.com/aparajita/capacitor-biometric-auth?tab=readme-ov-file#usage it is saying:

👉 NOTE: Your Android app must use a base theme named "AppTheme".

...but for me it works fine even though my app is not using "a base theme named "AppTheme"".

I found the following related issue #4 and it seems it has been fixed.

The question is whether it has really been fixed but the docs have not been updated accordingly or does the requirement related to AppTheme name still applies (which would imply the issue at the link from above is not related to this issue).

As I reported above for me it is working fine when I am using a theme that has name different then AppTheme.

Thanks!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.