1import { UnavailabilityError } from 'expo-modules-core';
2import invariant from 'invariant';
3
4import ExpoLocalAuthentication from './ExpoLocalAuthentication';
5import {
6  LocalAuthenticationOptions,
7  AuthenticationType,
8  LocalAuthenticationResult,
9  SecurityLevel,
10} from './LocalAuthentication.types';
11
12export { LocalAuthenticationOptions, AuthenticationType, LocalAuthenticationResult, SecurityLevel };
13
14// @needsAudit
15/**
16 * Determine whether a face or fingerprint scanner is available on the device.
17 * @return Returns a promise which fulfils with a `boolean` value indicating whether a face or
18 * fingerprint scanner is available on this device.
19 */
20export async function hasHardwareAsync(): Promise<boolean> {
21  if (!ExpoLocalAuthentication.hasHardwareAsync) {
22    throw new UnavailabilityError('expo-local-authentication', 'hasHardwareAsync');
23  }
24  return await ExpoLocalAuthentication.hasHardwareAsync();
25}
26
27// @needsAudit
28/**
29 * Determine what kinds of authentications are available on the device.
30 * @return Returns a promise which fulfils to an array containing [`AuthenticationType`s](#authenticationtype).
31 *
32 * Devices can support multiple authentication methods- i.e. `[1,2]` means the device supports both
33 * fingerprint and facial recognition. If none are supported, this method returns an empty array.
34 */
35export async function supportedAuthenticationTypesAsync(): Promise<AuthenticationType[]> {
36  if (!ExpoLocalAuthentication.supportedAuthenticationTypesAsync) {
37    throw new UnavailabilityError('expo-local-authentication', 'supportedAuthenticationTypesAsync');
38  }
39  return await ExpoLocalAuthentication.supportedAuthenticationTypesAsync();
40}
41
42// @needsAudit
43/**
44 * Determine whether the device has saved fingerprints or facial data to use for authentication.
45 * @return Returns a promise which fulfils to `boolean` value indicating whether the device has
46 * saved fingerprints or facial data for authentication.
47 */
48export async function isEnrolledAsync(): Promise<boolean> {
49  if (!ExpoLocalAuthentication.isEnrolledAsync) {
50    throw new UnavailabilityError('expo-local-authentication', 'isEnrolledAsync');
51  }
52  return await ExpoLocalAuthentication.isEnrolledAsync();
53}
54
55// @needsAudit
56/**
57 * Determine what kind of authentication is enrolled on the device.
58 * @return Returns a promise which fulfils with [`SecurityLevel`](#securitylevel).
59 * > **Note:** On Android devices prior to M, `SECRET` can be returned if only the SIM lock has been
60 * enrolled, which is not the method that [`authenticateAsync`](#localauthenticationauthenticateasyncoptions)
61 * prompts.
62 */
63export async function getEnrolledLevelAsync(): Promise<SecurityLevel> {
64  if (!ExpoLocalAuthentication.getEnrolledLevelAsync) {
65    throw new UnavailabilityError('expo-local-authentication', 'getEnrolledLevelAsync');
66  }
67  return await ExpoLocalAuthentication.getEnrolledLevelAsync();
68}
69
70// @needsAudit
71/**
72 * Attempts to authenticate via Fingerprint/TouchID (or FaceID if available on the device).
73 * > **Note:** Apple requires apps which use FaceID to provide a description of why they use this API.
74 * If you try to use FaceID on an iPhone with FaceID without providing `infoPlist.NSFaceIDUsageDescription`
75 * in `app.json`, the module will authenticate using device passcode. For more information about
76 * usage descriptions on iOS, see [permissions guide](/guides/permissions/#ios).
77 * @param options
78 * @return Returns a promise which fulfils with [`LocalAuthenticationResult`](#localauthenticationresult).
79 */
80export async function authenticateAsync(
81  options: LocalAuthenticationOptions = {}
82): Promise<LocalAuthenticationResult> {
83  if (!ExpoLocalAuthentication.authenticateAsync) {
84    throw new UnavailabilityError('expo-local-authentication', 'authenticateAsync');
85  }
86
87  if (options.hasOwnProperty('promptMessage')) {
88    invariant(
89      typeof options.promptMessage === 'string' && options.promptMessage.length,
90      'LocalAuthentication.authenticateAsync : `options.promptMessage` must be a non-empty string.'
91    );
92  }
93
94  const promptMessage = options.promptMessage || 'Authenticate';
95  const result = await ExpoLocalAuthentication.authenticateAsync({ ...options, promptMessage });
96
97  return result;
98}
99
100// @needsAudit
101/**
102 * Cancels authentication flow.
103 * @platform android
104 */
105export async function cancelAuthenticate(): Promise<void> {
106  if (!ExpoLocalAuthentication.cancelAuthenticate) {
107    throw new UnavailabilityError('expo-local-authentication', 'cancelAuthenticate');
108  }
109  await ExpoLocalAuthentication.cancelAuthenticate();
110}
111