@aparajita/capacitor-biometric-auth
Advanced tools
Comparing version 5.2.1 to 6.0.0
@@ -7,4 +7,5 @@ import { WebPlugin } from '@capacitor/core'; | ||
abstract checkBiometry(): Promise<CheckBiometryResult>; | ||
abstract authenticate(options?: AuthenticateOptions): Promise<void>; | ||
authenticate(options?: AuthenticateOptions): Promise<void>; | ||
protected abstract internalAuthenticate(options?: AuthenticateOptions): Promise<void>; | ||
addResumeListener(listener: ResumeListener): Promise<PluginListenerHandle> & PluginListenerHandle; | ||
} |
import { App } from '@capacitor/app'; | ||
import { WebPlugin } from '@capacitor/core'; | ||
import { CapacitorException, WebPlugin } from '@capacitor/core'; | ||
import { BiometryError } from './definitions'; | ||
// eslint-disable-next-line import/prefer-default-export | ||
export class BiometricAuthBase extends WebPlugin { | ||
async authenticate(options) { | ||
try { | ||
await this.internalAuthenticate(options); | ||
} | ||
catch (error) { | ||
// error will be an instance of CapacitorException on native platforms, | ||
// an instance of BiometryError on the web. | ||
if (error instanceof CapacitorException) { | ||
throw new BiometryError(error.message, | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any | ||
error.data.code); | ||
} | ||
else { | ||
throw error; | ||
} | ||
} | ||
} | ||
addResumeListener(listener) { | ||
@@ -6,0 +24,0 @@ return App.addListener('appStateChange', ({ isActive }) => { |
@@ -1,2 +0,2 @@ | ||
import type { PluginListenerHandle, PluginResultError, WebPlugin } from '@capacitor/core'; | ||
import type { PluginListenerHandle, WebPlugin } from '@capacitor/core'; | ||
export declare enum BiometryType { | ||
@@ -104,7 +104,5 @@ /** | ||
/** | ||
* For information on this setting, see: | ||
* If not set, defaults to true. | ||
* | ||
* https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean) | ||
* | ||
* If not set, defaults to true. | ||
* For information on this setting, see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean). | ||
*/ | ||
@@ -114,4 +112,4 @@ androidConfirmationRequired?: boolean; | ||
/** | ||
* If the `authenticate()` method throws an exception, the error object | ||
* contains a .code property which will contain one of these strings, | ||
* If the `authenticate()` method throws an exception, the `BiometryError` | ||
* instance contains a .code property which will contain one of these strings, | ||
* indicating what the error was. | ||
@@ -137,8 +135,8 @@ * | ||
} | ||
export interface ResultError extends PluginResultError { | ||
code: string; | ||
} | ||
export declare class BiometryError implements ResultError { | ||
/** | ||
* `authenticate()` throws instances of this class. | ||
*/ | ||
export declare class BiometryError { | ||
message: string; | ||
code: string; | ||
code: BiometryErrorType; | ||
constructor(message: string, code: BiometryErrorType); | ||
@@ -175,8 +173,3 @@ } | ||
* `BiometryErrorType` enum values, and is consistent across | ||
* platforms. This allows you to check for specific errors in a platform- | ||
* independent way, for example: | ||
* | ||
* if (result.code === BiometryErrorType.biometryNotEnrolled) { | ||
* ... | ||
* } | ||
* platforms. | ||
*/ | ||
@@ -189,2 +182,5 @@ code: BiometryErrorType; | ||
export type ResumeListener = (info: CheckBiometryResult) => void; | ||
/** | ||
* This is the public interface of the plugin. | ||
*/ | ||
export interface BiometricAuthPlugin extends WebPlugin { | ||
@@ -226,4 +222,7 @@ /** | ||
* The function will be passed the result of `checkBiometry()`. | ||
* | ||
* 👉 **NOTE:** checkBiometry() must be called at least once | ||
* before calling this method. | ||
*/ | ||
addResumeListener: (listener: ResumeListener) => Promise<PluginListenerHandle>; | ||
} |
@@ -0,1 +1,2 @@ | ||
// noinspection JSUnusedGlobalSymbols | ||
export var BiometryType; | ||
@@ -29,4 +30,4 @@ (function (BiometryType) { | ||
/** | ||
* If the `authenticate()` method throws an exception, the error object | ||
* contains a .code property which will contain one of these strings, | ||
* If the `authenticate()` method throws an exception, the `BiometryError` | ||
* instance contains a .code property which will contain one of these strings, | ||
* indicating what the error was. | ||
@@ -53,2 +54,5 @@ * | ||
})(BiometryErrorType || (BiometryErrorType = {})); | ||
/** | ||
* `authenticate()` throws instances of this class. | ||
*/ | ||
export class BiometryError { | ||
@@ -55,0 +59,0 @@ constructor(message, code) { |
@@ -7,4 +7,4 @@ import { BiometricAuthBase } from './base'; | ||
checkBiometry(): Promise<CheckBiometryResult>; | ||
authenticate(options?: AuthenticateOptions): Promise<void>; | ||
internalAuthenticate(options?: AuthenticateOptions): Promise<void>; | ||
setBiometryType(type: BiometryType | string | undefined): Promise<void>; | ||
} |
@@ -7,7 +7,20 @@ import { BiometricAuthBase } from './base'; | ||
super(); | ||
this.checkBiometry = capProxy.checkBiometry; | ||
this.authenticate = capProxy.authenticate; | ||
/* | ||
In order to call native methods and maintain the ability to | ||
call pure Javascript methods as well, we have to bind the native methods | ||
to the proxy. | ||
capProxy is a proxy of an instance of this class, so it is safe | ||
to cast it to this class. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
const proxy = capProxy; | ||
/* eslint-disable @typescript-eslint/unbound-method */ | ||
this.checkBiometry = proxy.checkBiometry; | ||
this.internalAuthenticate = proxy.internalAuthenticate; | ||
/* eslint-enable */ | ||
} | ||
// @native | ||
async checkBiometry() { | ||
// Never used, satisfy the compiler | ||
// Never used, but we have to satisfy the compiler. | ||
return Promise.resolve({ | ||
@@ -21,4 +34,8 @@ isAvailable: true, | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function | ||
async authenticate(options) { } | ||
// @native | ||
// On native platforms, this will present the native authentication UI. | ||
async internalAuthenticate( | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
options) { } | ||
// Web only, used for simulating biometric authentication. | ||
// eslint-disable-next-line @typescript-eslint/require-await | ||
@@ -28,4 +45,4 @@ async setBiometryType( | ||
type) { | ||
throw this.unimplemented('setBiometryType is web only'); | ||
throw this.unimplemented('setBiometryType() is web only'); | ||
} | ||
} |
@@ -7,4 +7,4 @@ import { BiometricAuthBase } from './base'; | ||
checkBiometry(): Promise<CheckBiometryResult>; | ||
authenticate(options?: AuthenticateOptions): Promise<void>; | ||
internalAuthenticate(options?: AuthenticateOptions): Promise<void>; | ||
setBiometryType(type: BiometryType | string | undefined): Promise<void>; | ||
} |
@@ -10,2 +10,3 @@ import { BiometricAuthBase } from './base'; | ||
} | ||
// On the web, return the fake biometry set by setBiometryType(). | ||
async checkBiometry() { | ||
@@ -20,16 +21,19 @@ return Promise.resolve({ | ||
} | ||
async authenticate(options) { | ||
return this.checkBiometry().then(({ isAvailable, biometryType }) => { | ||
var _a; | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('User cancelled', BiometryErrorType.userCancel); | ||
// On the web, fake authentication with a confirm dialog. | ||
async internalAuthenticate(options) { | ||
const { isAvailable, biometryType } = await this.checkBiometry(); | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm( | ||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string | ||
(options === null || options === void 0 ? void 0 : options.reason) || | ||
`Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('Biometry not available', BiometryErrorType.biometryNotAvailable); | ||
}); | ||
throw new BiometryError('User cancelled', BiometryErrorType.userCancel); | ||
} | ||
throw new BiometryError('Biometry not available', BiometryErrorType.biometryNotAvailable); | ||
} | ||
// Web only, used for simulating biometric authentication. | ||
async setBiometryType(type) { | ||
@@ -36,0 +40,0 @@ if (typeof type === 'undefined') { |
@@ -6,2 +6,3 @@ 'use strict'; | ||
// noinspection JSUnusedGlobalSymbols | ||
exports.BiometryType = void 0; | ||
@@ -35,4 +36,4 @@ (function (BiometryType) { | ||
/** | ||
* If the `authenticate()` method throws an exception, the error object | ||
* contains a .code property which will contain one of these strings, | ||
* If the `authenticate()` method throws an exception, the `BiometryError` | ||
* instance contains a .code property which will contain one of these strings, | ||
* indicating what the error was. | ||
@@ -59,2 +60,5 @@ * | ||
})(exports.BiometryErrorType || (exports.BiometryErrorType = {})); | ||
/** | ||
* `authenticate()` throws instances of this class. | ||
*/ | ||
class BiometryError { | ||
@@ -91,2 +95,19 @@ constructor(message, code) { | ||
class BiometricAuthBase extends core.WebPlugin { | ||
async authenticate(options) { | ||
try { | ||
await this.internalAuthenticate(options); | ||
} | ||
catch (error) { | ||
// error will be an instance of CapacitorException on native platforms, | ||
// an instance of BiometryError on the web. | ||
if (error instanceof core.CapacitorException) { | ||
throw new BiometryError(error.message, | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any | ||
error.data.code); | ||
} | ||
else { | ||
throw error; | ||
} | ||
} | ||
} | ||
addResumeListener(listener) { | ||
@@ -111,2 +132,3 @@ return app.App.addListener('appStateChange', ({ isActive }) => { | ||
} | ||
// On the web, return the fake biometry set by setBiometryType(). | ||
async checkBiometry() { | ||
@@ -121,16 +143,19 @@ return Promise.resolve({ | ||
} | ||
async authenticate(options) { | ||
return this.checkBiometry().then(({ isAvailable, biometryType }) => { | ||
var _a; | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel); | ||
// On the web, fake authentication with a confirm dialog. | ||
async internalAuthenticate(options) { | ||
const { isAvailable, biometryType } = await this.checkBiometry(); | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm( | ||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string | ||
(options === null || options === void 0 ? void 0 : options.reason) || | ||
`Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable); | ||
}); | ||
throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel); | ||
} | ||
throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable); | ||
} | ||
// Web only, used for simulating biometric authentication. | ||
async setBiometryType(type) { | ||
@@ -163,7 +188,20 @@ if (typeof type === 'undefined') { | ||
super(); | ||
this.checkBiometry = capProxy.checkBiometry; | ||
this.authenticate = capProxy.authenticate; | ||
/* | ||
In order to call native methods and maintain the ability to | ||
call pure Javascript methods as well, we have to bind the native methods | ||
to the proxy. | ||
capProxy is a proxy of an instance of this class, so it is safe | ||
to cast it to this class. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
const proxy = capProxy; | ||
/* eslint-disable @typescript-eslint/unbound-method */ | ||
this.checkBiometry = proxy.checkBiometry; | ||
this.internalAuthenticate = proxy.internalAuthenticate; | ||
/* eslint-enable */ | ||
} | ||
// @native | ||
async checkBiometry() { | ||
// Never used, satisfy the compiler | ||
// Never used, but we have to satisfy the compiler. | ||
return Promise.resolve({ | ||
@@ -177,4 +215,8 @@ isAvailable: true, | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function | ||
async authenticate(options) { } | ||
// @native | ||
// On native platforms, this will present the native authentication UI. | ||
async internalAuthenticate( | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
options) { } | ||
// Web only, used for simulating biometric authentication. | ||
// eslint-disable-next-line @typescript-eslint/require-await | ||
@@ -184,3 +226,3 @@ async setBiometryType( | ||
type) { | ||
throw this.unimplemented('setBiometryType is web only'); | ||
throw this.unimplemented('setBiometryType() is web only'); | ||
} | ||
@@ -187,0 +229,0 @@ } |
var capacitorBiometricAuth = (function (exports, core, app) { | ||
'use strict'; | ||
// noinspection JSUnusedGlobalSymbols | ||
exports.BiometryType = void 0; | ||
@@ -32,4 +33,4 @@ (function (BiometryType) { | ||
/** | ||
* If the `authenticate()` method throws an exception, the error object | ||
* contains a .code property which will contain one of these strings, | ||
* If the `authenticate()` method throws an exception, the `BiometryError` | ||
* instance contains a .code property which will contain one of these strings, | ||
* indicating what the error was. | ||
@@ -56,2 +57,5 @@ * | ||
})(exports.BiometryErrorType || (exports.BiometryErrorType = {})); | ||
/** | ||
* `authenticate()` throws instances of this class. | ||
*/ | ||
class BiometryError { | ||
@@ -88,2 +92,19 @@ constructor(message, code) { | ||
class BiometricAuthBase extends core.WebPlugin { | ||
async authenticate(options) { | ||
try { | ||
await this.internalAuthenticate(options); | ||
} | ||
catch (error) { | ||
// error will be an instance of CapacitorException on native platforms, | ||
// an instance of BiometryError on the web. | ||
if (error instanceof core.CapacitorException) { | ||
throw new BiometryError(error.message, | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any | ||
error.data.code); | ||
} | ||
else { | ||
throw error; | ||
} | ||
} | ||
} | ||
addResumeListener(listener) { | ||
@@ -108,2 +129,3 @@ return app.App.addListener('appStateChange', ({ isActive }) => { | ||
} | ||
// On the web, return the fake biometry set by setBiometryType(). | ||
async checkBiometry() { | ||
@@ -118,16 +140,19 @@ return Promise.resolve({ | ||
} | ||
async authenticate(options) { | ||
return this.checkBiometry().then(({ isAvailable, biometryType }) => { | ||
var _a; | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel); | ||
// On the web, fake authentication with a confirm dialog. | ||
async internalAuthenticate(options) { | ||
const { isAvailable, biometryType } = await this.checkBiometry(); | ||
if (isAvailable) { | ||
if ( | ||
// eslint-disable-next-line no-alert | ||
confirm( | ||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string | ||
(options === null || options === void 0 ? void 0 : options.reason) || | ||
`Authenticate with ${getBiometryName(biometryType)}?`)) { | ||
return; | ||
} | ||
throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable); | ||
}); | ||
throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel); | ||
} | ||
throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable); | ||
} | ||
// Web only, used for simulating biometric authentication. | ||
async setBiometryType(type) { | ||
@@ -160,7 +185,20 @@ if (typeof type === 'undefined') { | ||
super(); | ||
this.checkBiometry = capProxy.checkBiometry; | ||
this.authenticate = capProxy.authenticate; | ||
/* | ||
In order to call native methods and maintain the ability to | ||
call pure Javascript methods as well, we have to bind the native methods | ||
to the proxy. | ||
capProxy is a proxy of an instance of this class, so it is safe | ||
to cast it to this class. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions | ||
const proxy = capProxy; | ||
/* eslint-disable @typescript-eslint/unbound-method */ | ||
this.checkBiometry = proxy.checkBiometry; | ||
this.internalAuthenticate = proxy.internalAuthenticate; | ||
/* eslint-enable */ | ||
} | ||
// @native | ||
async checkBiometry() { | ||
// Never used, satisfy the compiler | ||
// Never used, but we have to satisfy the compiler. | ||
return Promise.resolve({ | ||
@@ -174,4 +212,8 @@ isAvailable: true, | ||
} | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function | ||
async authenticate(options) { } | ||
// @native | ||
// On native platforms, this will present the native authentication UI. | ||
async internalAuthenticate( | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
options) { } | ||
// Web only, used for simulating biometric authentication. | ||
// eslint-disable-next-line @typescript-eslint/require-await | ||
@@ -181,3 +223,3 @@ async setBiometryType( | ||
type) { | ||
throw this.unimplemented('setBiometryType is web only'); | ||
throw this.unimplemented('setBiometryType() is web only'); | ||
} | ||
@@ -184,0 +226,0 @@ } |
{ | ||
"name": "@aparajita/capacitor-biometric-auth", | ||
"version": "5.2.1", | ||
"version": "6.0.0", | ||
"description": "Provides access to the native biometric auth APIs for Capacitor apps", | ||
@@ -12,3 +12,3 @@ "author": "Aparajita Fishman", | ||
"engines": { | ||
"node": ">=16.15.1" | ||
"node": ">=18" | ||
}, | ||
@@ -60,23 +60,23 @@ "files": [ | ||
"@aparajita/swiftly": "^1.0.4", | ||
"@capacitor/cli": "^5.4.2", | ||
"@commitlint/cli": "^17.7.2", | ||
"@commitlint/config-conventional": "^17.7.0", | ||
"@capacitor/cli": "^5.5.1", | ||
"@commitlint/cli": "^18.4.1", | ||
"@commitlint/config-conventional": "^18.4.0", | ||
"@ionic/swiftlint-config": "^1.1.2", | ||
"@rollup/plugin-json": "^6.0.1", | ||
"@types/node": "^20.8.4", | ||
"@typescript-eslint/eslint-plugin": "^6.7.5", | ||
"@typescript-eslint/parser": "^6.7.5", | ||
"commit-and-tag-version": "^11.3.0", | ||
"eslint": "^8.51.0", | ||
"@types/node": "^20.9.0", | ||
"@typescript-eslint/eslint-plugin": "^6.11.0", | ||
"@typescript-eslint/parser": "^6.11.0", | ||
"commit-and-tag-version": "^12.0.0", | ||
"eslint": "^8.53.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"eslint-config-standard": "^17.1.0", | ||
"eslint-import-resolver-typescript": "^3.6.1", | ||
"eslint-plugin-import": "^2.28.1", | ||
"eslint-plugin-n": "^16.2.0", | ||
"eslint-plugin-import": "^2.29.0", | ||
"eslint-plugin-n": "^16.3.1", | ||
"eslint-plugin-promise": "^6.1.1", | ||
"nodemon": "^3.0.1", | ||
"prettier": "^3.0.3", | ||
"prettier-plugin-java": "^2.3.1", | ||
"prettier": "^3.1.0", | ||
"prettier-plugin-java": "^2.4.0", | ||
"rimraf": "^5.0.5", | ||
"rollup": "^3.29.4", | ||
"rollup": "^4.4.0", | ||
"swiftlint": "^1.0.2", | ||
@@ -86,6 +86,6 @@ "typescript": "~5.2.2" | ||
"dependencies": { | ||
"@capacitor/android": "^5.4.2", | ||
"@capacitor/android": "^5.5.1", | ||
"@capacitor/app": "^5.0.6", | ||
"@capacitor/core": "^5.4.2", | ||
"@capacitor/ios": "^5.4.2" | ||
"@capacitor/core": "^5.5.1", | ||
"@capacitor/ios": "^5.5.1" | ||
}, | ||
@@ -92,0 +92,0 @@ "scripts": { |
108
README.md
@@ -9,3 +9,3 @@ <div class="markdown-body"> | ||
🛑 **BREAKING CHANGE:** If you are upgrading from a version prior to 3.0.0, please note that `androidMaxAttempts` is no longer supported. See the documentation for [`authenticate()`](#authenticate) for more information. | ||
🛑 **BREAKING CHANGE:** If you are upgrading from a version prior to 6.0.0, please note that [`authenticate()`](#authenticate) now throws an instance of `BiometryError`, and `BiometryError.code` is now typed as [`BiometryErrorType`](#biometryerrortype). | ||
@@ -28,2 +28,12 @@ ## Demos | ||
### 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 | ||
@@ -33,15 +43,51 @@ | ||
> **NOTE:** Your Android app must use a base theme named "AppTheme". | ||
👉 **NOTE:** Your Android app must use a base theme named "AppTheme". | ||
### Checking availability | ||
Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note that `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. | ||
Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry()`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note the following: | ||
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`](#addresumelistener) you can register a callback that is passed a [`CheckBiometryResult`](#checkbiometryresult) when your app resumes. | ||
- `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. | ||
- `biometryTypes` may contain more than one type of biometry. This occurs on Android devices that support multiple types of biometry. In such cases the `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()`](#addresumelistener) you can register a callback that is passed a [`CheckBiometryResult`](#checkbiometryresult) when your app resumes. | ||
#### Example | ||
```typescript | ||
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 | ||
Once you have determined that biometry is available, to initiate biometric authentication call [`authenticate`](#authenticate). `authenticate` takes an [`AuthenticateOptions`](#authenticateoptions) object which you will want to use in order to control the behavior and appearance of the biometric prompt. | ||
To initiate biometric authentication call [`authenticate()`](#authenticate). `authenticate` takes an [`AuthenticateOptions`](#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 a `BiometryError`, which has two properties: | ||
If authentication succeeds, the Promise resolves. If authentication fails, the Promise is rejected with an instance of [`BiometryError`](#biometryerror), which has two properties: | ||
@@ -53,2 +99,30 @@ | Property | Type | Description | | ||
#### Example | ||
```typescript | ||
import { 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, | ||
}) | ||
} 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 | ||
@@ -66,3 +140,3 @@ | ||
On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will only present the primary type, which is determined by the system. | ||
On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will only present the primary (most secure) type, which is determined by the system. | ||
@@ -85,2 +159,4 @@ ## API | ||
This is the public interface of the plugin. | ||
### checkBiometry() | ||
@@ -132,3 +208,3 @@ | ||
Register a function that will be called when the app resumes. The function will be passed the result of `checkBiometry()`. | ||
Register a function that will be called when the app resumes. The function will be passed the result of `checkBiometry()`.<br><br>👉 **NOTE:** checkBiometry() must be called at least once before calling this method. | ||
@@ -147,9 +223,9 @@ | Param | Type | | ||
| Prop | Type | Description | | ||
| :------------ | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| isAvailable | boolean | True if the device has biometric authentication capability and the current user has enrolled in some form of biometry. | | ||
| biometryType | <a href="#biometrytype">BiometryType</a> | The primary type of biometry available on the device. If the device supports both fingerprint and face authentication, this will be <a href="#biometrytype">`BiometryType.touchId`</a>. | | ||
| biometryTypes | BiometryType[] | All of the biometry types supported by the device (currently only Android devices support multiple biometry types). If no biometry is available, this will be an empty array. If multiple types are supported, Android only guarantees that one of them is actually available. | | ||
| reason | string | If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. | | ||
| code | <a href="#biometryerrortype">BiometryErrorType</a> | If 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 <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. This allows you to check for specific errors in a platform- independent way, for example:<br><br>if (result.code === <a href="#biometryerrortype">BiometryErrorType.biometryNotEnrolled</a>) { ... } | | ||
| Prop | Type | Description | | ||
| :------------ | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| isAvailable | boolean | True if the device has biometric authentication capability and the current user has enrolled in some form of biometry. | | ||
| biometryType | <a href="#biometrytype">BiometryType</a> | The primary type of biometry available on the device. If the device supports both fingerprint and face authentication, this will be <a href="#biometrytype">`BiometryType.touchId`</a>. | | ||
| biometryTypes | BiometryType[] | All of the biometry types supported by the device (currently only Android devices support multiple biometry types). If no biometry is available, this will be an empty array. If multiple types are supported, Android only guarantees that one of them is actually available. | | ||
| reason | string | If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. | | ||
| code | <a href="#biometryerrortype">BiometryErrorType</a> | If 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 <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. | | ||
@@ -166,3 +242,3 @@ #### AuthenticateOptions | ||
| androidSubtitle | string | Subtitle for the Android dialog. If not supplied, the system default is used. | | ||
| androidConfirmationRequired | boolean | For information on this setting, see:<br><br>https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean)<br><br>If not set, defaults to true. | | ||
| androidConfirmationRequired | boolean | If not set, defaults to true.<br><br>For information on this setting, see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean). | | ||
@@ -169,0 +245,0 @@ #### PluginListenerHandle |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
96626
914
283
Updated@capacitor/android@^5.5.1
Updated@capacitor/core@^5.5.1
Updated@capacitor/ios@^5.5.1