@symbiote-native/local-auth 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +171 -36
- package/build/core/local-authentication.js +2 -1
- package/build-ngc/core/local-authentication.js +2 -1
- package/build-ngc/core/local-authentication.js.map +1 -1
- package/build-ngc/core/native-module.js.map +1 -1
- package/package.json +46 -20
- package/src/core/local-authentication.ts +9 -3
- package/src/core/native-module.ts +8 -4
- package/build/react/index.d.ts +0 -1
- package/build/react/index.js +0 -6
- package/build/svelte/index.d.ts +0 -1
- package/build/svelte/index.js +0 -6
- package/build/vue/index.d.ts +0 -1
- package/build/vue/index.js +0 -4
- package/src/react/index.ts +0 -6
- package/src/svelte/index.ts +0 -6
- package/src/vue/index.ts +0 -4
package/README.md
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
4
|
[`expo-local-authentication`](https://github.com/expo/expo/tree/main/packages/expo-local-authentication)
|
|
5
5
|
— FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android — usable from **every**
|
|
6
|
-
adapter, React, Vue, and Angular, not just React. Unlike this repo's other Expo wrapper
|
|
6
|
+
adapter, React, Vue, Svelte, Solid, and Angular, not just React. Unlike this repo's other Expo wrapper
|
|
7
7
|
([`@symbiote-native/sensors`](../sensors), an `EventEmitter` + live-subscription surface),
|
|
8
8
|
every function here is a one-shot async call with no per-instance state, so there is no hook/
|
|
9
|
-
composable/service to wrap — the React, Vue, and Angular entry points are plain
|
|
10
|
-
the same `core`.
|
|
9
|
+
composable/service to wrap — the React, Vue, Svelte, Solid, and Angular entry points are plain
|
|
10
|
+
re-exports of the same `core`.
|
|
11
11
|
|
|
12
12
|
## Install
|
|
13
13
|
|
|
@@ -26,12 +26,12 @@ Unlike a plain RN native module, `expo-local-authentication`'s native code is di
|
|
|
26
26
|
into the native host app **once**, covering this package and every other
|
|
27
27
|
`expo-modules-core` package with zero further changes:
|
|
28
28
|
|
|
29
|
-
| Platform | Touches
|
|
30
|
-
|
|
31
|
-
| iOS
|
|
32
|
-
| iOS
|
|
33
|
-
| Android
|
|
34
|
-
| Android
|
|
29
|
+
| Platform | Touches |
|
|
30
|
+
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| iOS | `ios/Podfile` — add `use_expo_modules!` |
|
|
32
|
+
| iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
|
|
33
|
+
| Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
|
|
34
|
+
| Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
35
35
|
|
|
36
36
|
Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
|
|
37
37
|
implementation: `examples/expo-react/ios/Podfile` and
|
|
@@ -52,13 +52,13 @@ src/core/ hasHardwareAsync / isEnrolledAsync / getEnrolledLevelAsync /
|
|
|
52
52
|
AuthenticationType, SecurityLevel, and the option/result/error types.
|
|
53
53
|
native-module.ts resolves the native module via expo-modules-core's
|
|
54
54
|
requireNativeModule.
|
|
55
|
-
src/react/ @symbiote-native/local-auth/react — export * from '../core'
|
|
56
|
-
src/vue/ @symbiote-native/local-auth/vue — export * from '../core'
|
|
57
55
|
src/angular/ @symbiote-native/local-auth/angular — export * from '../core'
|
|
58
56
|
```
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto
|
|
59
|
+
`src/core/` — no physical per-framework file, since there's nothing to subscribe to or clean up.
|
|
60
|
+
`./angular` stays a physical file/subpath since Angular ships through a separate `ngc`/AOT build
|
|
61
|
+
(`build-ngc/`).
|
|
62
62
|
|
|
63
63
|
## Use it
|
|
64
64
|
|
|
@@ -77,7 +77,8 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/rea
|
|
|
77
77
|
function LocalAuthScreen() {
|
|
78
78
|
const [hasHardware, setHasHardware] = useState(false);
|
|
79
79
|
const [isEnrolled, setIsEnrolled] = useState(false);
|
|
80
|
-
const [authResult, setAuthResult] =
|
|
80
|
+
const [authResult, setAuthResult] =
|
|
81
|
+
useState<ILocalAuthenticationResult | null>(null);
|
|
81
82
|
|
|
82
83
|
useEffect(() => {
|
|
83
84
|
hasHardwareAsync().then(setHasHardware);
|
|
@@ -85,12 +86,18 @@ function LocalAuthScreen() {
|
|
|
85
86
|
}, []);
|
|
86
87
|
|
|
87
88
|
const handleAuthenticate = () => {
|
|
88
|
-
authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
|
|
89
|
+
authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
|
|
90
|
+
setAuthResult,
|
|
91
|
+
);
|
|
89
92
|
};
|
|
90
93
|
|
|
91
94
|
return (
|
|
92
95
|
<View>
|
|
93
|
-
<Text>
|
|
96
|
+
<Text>
|
|
97
|
+
{hasHardware && isEnrolled
|
|
98
|
+
? 'Ready to authenticate'
|
|
99
|
+
: 'No biometrics enrolled'}
|
|
100
|
+
</Text>
|
|
94
101
|
<Pressable onPress={handleAuthenticate}>
|
|
95
102
|
<Text>Authenticate</Text>
|
|
96
103
|
</Pressable>
|
|
@@ -99,7 +106,11 @@ function LocalAuthScreen() {
|
|
|
99
106
|
<Text>Cancel</Text>
|
|
100
107
|
</Pressable>
|
|
101
108
|
)}
|
|
102
|
-
{authResult &&
|
|
109
|
+
{authResult && (
|
|
110
|
+
<Text>
|
|
111
|
+
{authResult.success ? 'Success' : `Failed: ${authResult.error}`}
|
|
112
|
+
</Text>
|
|
113
|
+
)}
|
|
103
114
|
</View>
|
|
104
115
|
);
|
|
105
116
|
}
|
|
@@ -136,18 +147,126 @@ function handleAuthenticate(): void {
|
|
|
136
147
|
|
|
137
148
|
<template>
|
|
138
149
|
<View>
|
|
139
|
-
<Text>{{
|
|
150
|
+
<Text>{{
|
|
151
|
+
hasHardware && isEnrolled
|
|
152
|
+
? 'Ready to authenticate'
|
|
153
|
+
: 'No biometrics enrolled'
|
|
154
|
+
}}</Text>
|
|
140
155
|
<Pressable @press="handleAuthenticate">
|
|
141
156
|
<Text>Authenticate</Text>
|
|
142
157
|
</Pressable>
|
|
143
158
|
<Pressable v-if="Platform.OS === 'android'" @press="cancelAuthenticate">
|
|
144
159
|
<Text>Cancel</Text>
|
|
145
160
|
</Pressable>
|
|
146
|
-
<Text v-if="authResult">{{
|
|
161
|
+
<Text v-if="authResult">{{
|
|
162
|
+
authResult.success ? 'Success' : `Failed: ${authResult.error}`
|
|
163
|
+
}}</Text>
|
|
147
164
|
</View>
|
|
148
165
|
</template>
|
|
149
166
|
```
|
|
150
167
|
|
|
168
|
+
```svelte
|
|
169
|
+
<!-- Svelte -->
|
|
170
|
+
<script lang="ts">
|
|
171
|
+
import { Platform, Pressable, Text, View } from '@symbiote-native/svelte';
|
|
172
|
+
import {
|
|
173
|
+
authenticateAsync,
|
|
174
|
+
cancelAuthenticate,
|
|
175
|
+
hasHardwareAsync,
|
|
176
|
+
isEnrolledAsync,
|
|
177
|
+
} from '@symbiote-native/local-auth/svelte';
|
|
178
|
+
import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/svelte';
|
|
179
|
+
|
|
180
|
+
let hasHardware = $state(false);
|
|
181
|
+
let isEnrolled = $state(false);
|
|
182
|
+
let authResult = $state<ILocalAuthenticationResult | null>(null);
|
|
183
|
+
|
|
184
|
+
$effect(() => {
|
|
185
|
+
hasHardwareAsync().then(value => (hasHardware = value));
|
|
186
|
+
isEnrolledAsync().then(value => (isEnrolled = value));
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
function handleAuthenticate(): void {
|
|
190
|
+
authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value => {
|
|
191
|
+
authResult = value;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
</script>
|
|
195
|
+
|
|
196
|
+
<View>
|
|
197
|
+
<Text>
|
|
198
|
+
{hasHardware && isEnrolled ? 'Ready to authenticate' : 'No biometrics enrolled'}
|
|
199
|
+
</Text>
|
|
200
|
+
<Pressable onPress={handleAuthenticate}>
|
|
201
|
+
<Text>Authenticate</Text>
|
|
202
|
+
</Pressable>
|
|
203
|
+
{#if Platform.OS === 'android'}
|
|
204
|
+
<Pressable onPress={() => cancelAuthenticate()}>
|
|
205
|
+
<Text>Cancel</Text>
|
|
206
|
+
</Pressable>
|
|
207
|
+
{/if}
|
|
208
|
+
{#if authResult}
|
|
209
|
+
<Text>{authResult.success ? 'Success' : `Failed: ${authResult.error}`}</Text>
|
|
210
|
+
{/if}
|
|
211
|
+
</View>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
// Solid
|
|
216
|
+
import { Show, createSignal, onMount } from 'solid-js';
|
|
217
|
+
import { Platform, Pressable, Text, View } from '@symbiote-native/solid';
|
|
218
|
+
import {
|
|
219
|
+
authenticateAsync,
|
|
220
|
+
cancelAuthenticate,
|
|
221
|
+
hasHardwareAsync,
|
|
222
|
+
isEnrolledAsync,
|
|
223
|
+
} from '@symbiote-native/local-auth/solid';
|
|
224
|
+
import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/solid';
|
|
225
|
+
|
|
226
|
+
function LocalAuthScreen() {
|
|
227
|
+
const [hasHardware, setHasHardware] = createSignal(false);
|
|
228
|
+
const [isEnrolled, setIsEnrolled] = createSignal(false);
|
|
229
|
+
const [authResult, setAuthResult] =
|
|
230
|
+
createSignal<ILocalAuthenticationResult | null>(null);
|
|
231
|
+
|
|
232
|
+
onMount(() => {
|
|
233
|
+
void hasHardwareAsync().then(setHasHardware);
|
|
234
|
+
void isEnrolledAsync().then(setIsEnrolled);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const handleAuthenticate = () => {
|
|
238
|
+
void authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
|
|
239
|
+
setAuthResult,
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return (
|
|
244
|
+
<View>
|
|
245
|
+
<Text>
|
|
246
|
+
{hasHardware() && isEnrolled()
|
|
247
|
+
? 'Ready to authenticate'
|
|
248
|
+
: 'No biometrics enrolled'}
|
|
249
|
+
</Text>
|
|
250
|
+
<Pressable onPress={handleAuthenticate}>
|
|
251
|
+
<Text>Authenticate</Text>
|
|
252
|
+
</Pressable>
|
|
253
|
+
{Platform.OS === 'android' && (
|
|
254
|
+
<Pressable onPress={() => cancelAuthenticate()}>
|
|
255
|
+
<Text>Cancel</Text>
|
|
256
|
+
</Pressable>
|
|
257
|
+
)}
|
|
258
|
+
<Show when={authResult()} keyed>
|
|
259
|
+
{result => (
|
|
260
|
+
<Text>
|
|
261
|
+
{result.success ? 'Success' : `Failed: ${result.error}`}
|
|
262
|
+
</Text>
|
|
263
|
+
)}
|
|
264
|
+
</Show>
|
|
265
|
+
</View>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
151
270
|
```ts
|
|
152
271
|
// Angular
|
|
153
272
|
import { Component, signal } from '@angular/core';
|
|
@@ -165,7 +284,11 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
|
|
|
165
284
|
imports: [Pressable, Text, View],
|
|
166
285
|
template: `
|
|
167
286
|
<View>
|
|
168
|
-
<Text>{{
|
|
287
|
+
<Text>{{
|
|
288
|
+
hasHardware() && isEnrolled()
|
|
289
|
+
? 'Ready to authenticate'
|
|
290
|
+
: 'No biometrics enrolled'
|
|
291
|
+
}}</Text>
|
|
169
292
|
<Pressable (press)="handleAuthenticate()">
|
|
170
293
|
<Text>Authenticate</Text>
|
|
171
294
|
</Pressable>
|
|
@@ -175,7 +298,9 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
|
|
|
175
298
|
</Pressable>
|
|
176
299
|
}
|
|
177
300
|
@if (authResult(); as result) {
|
|
178
|
-
<Text>{{
|
|
301
|
+
<Text>{{
|
|
302
|
+
result.success ? 'Success' : 'Failed: ' + result.error
|
|
303
|
+
}}</Text>
|
|
179
304
|
}
|
|
180
305
|
</View>
|
|
181
306
|
`,
|
|
@@ -192,7 +317,9 @@ export class LocalAuthScreen {
|
|
|
192
317
|
}
|
|
193
318
|
|
|
194
319
|
handleAuthenticate(): void {
|
|
195
|
-
authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value =>
|
|
320
|
+
authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value =>
|
|
321
|
+
this.authResult.set(value),
|
|
322
|
+
);
|
|
196
323
|
}
|
|
197
324
|
|
|
198
325
|
handleCancel(): void {
|
|
@@ -202,17 +329,19 @@ export class LocalAuthScreen {
|
|
|
202
329
|
```
|
|
203
330
|
|
|
204
331
|
There's no per-instance service to `inject()` in the Angular case — every function is a plain
|
|
205
|
-
free function off the core package, called straight from the constructor.
|
|
206
|
-
trimmed from the real canary demo screens
|
|
332
|
+
free function off the core package, called straight from the constructor. The React/Vue/Angular
|
|
333
|
+
examples are trimmed from the real canary demo screens
|
|
334
|
+
(`examples/expo-react/screens/LocalAuthScreen.tsx`,
|
|
207
335
|
`examples/expo-vue-sfc/screens/LocalAuthScreen.vue`, `examples/expo-vue-tsx/screens/LocalAuthScreen.tsx`,
|
|
208
336
|
`examples/expo-angular/src/screens/LocalAuthScreen.ts`), which also cover
|
|
209
|
-
`getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card.
|
|
337
|
+
`getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card. No
|
|
338
|
+
canary demo screen exists yet for Svelte/Solid — the two snippets above follow the same surface.
|
|
210
339
|
|
|
211
340
|
## API
|
|
212
341
|
|
|
213
342
|
Free functions, no event stream, no per-instance state — upstream ships a handful of async
|
|
214
|
-
functions and two enums, not a subscribable sensor, so the React/Vue/Angular entry
|
|
215
|
-
are plain re-exports of `core` with nothing adapter-specific to add.
|
|
343
|
+
functions and two enums, not a subscribable sensor, so the React/Vue/Svelte/Solid/Angular entry
|
|
344
|
+
points above are plain re-exports of `core` with nothing adapter-specific to add.
|
|
216
345
|
|
|
217
346
|
```ts
|
|
218
347
|
hasHardwareAsync(): Promise<boolean>
|
|
@@ -229,10 +358,15 @@ from upstream's `LocalAuthentication.types.ts`, renamed with this repo's `I`-pre
|
|
|
229
358
|
for exported types (`ts-js-best-practices`).
|
|
230
359
|
|
|
231
360
|
```ts
|
|
232
|
-
import {
|
|
361
|
+
import {
|
|
362
|
+
authenticateAsync,
|
|
363
|
+
hasHardwareAsync,
|
|
364
|
+
} from '@symbiote-native/local-auth';
|
|
233
365
|
// or the framework-scoped entry points — identical surface, re-exported verbatim:
|
|
234
366
|
import { authenticateAsync } from '@symbiote-native/local-auth/react';
|
|
235
367
|
import { authenticateAsync } from '@symbiote-native/local-auth/vue';
|
|
368
|
+
import { authenticateAsync } from '@symbiote-native/local-auth/svelte';
|
|
369
|
+
import { authenticateAsync } from '@symbiote-native/local-auth/solid';
|
|
236
370
|
import { authenticateAsync } from '@symbiote-native/local-auth/angular';
|
|
237
371
|
```
|
|
238
372
|
|
|
@@ -241,7 +375,7 @@ import { authenticateAsync } from '@symbiote-native/local-auth/angular';
|
|
|
241
375
|
- **`not_enrolled` on Android almost always means the device's own lock screen has no PIN,
|
|
242
376
|
pattern, or password set.** A real symptom on a fresh emulator or factory-reset device:
|
|
243
377
|
`authenticateAsync` resolves `{ success: false, error: 'not_enrolled', warning:
|
|
244
|
-
|
|
378
|
+
'KeyguardManager#isDeviceSecure() returned false' }`. This is **not** a missing app
|
|
245
379
|
permission — the manifest permission this package needs is an ordinary build-time merge with
|
|
246
380
|
no runtime prompt, so there's nothing for your app to request. The fix lives on the device:
|
|
247
381
|
Settings → Security → Screen lock → set a PIN/pattern/password, then optionally enroll a
|
|
@@ -259,10 +393,11 @@ a view or per-instance state. Tests inject a fake native-module object in place
|
|
|
259
393
|
`src/core/types.test.ts`, `vitest`) — no `installFabric()`, no ViewConfig. Native rendering itself
|
|
260
394
|
is verified on-device (see the parent [README](../../README.md) for the project's testing model).
|
|
261
395
|
|
|
262
|
-
Native autolinking wiring is done in
|
|
263
|
-
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-
|
|
264
|
-
— iOS Podfile/`AppDelegate.swift` +
|
|
265
|
-
`MainApplication.kt` + `USE_BIOMETRIC`, all
|
|
266
|
-
package isn't yet demoed in the plain, non-Expo
|
|
267
|
-
|
|
268
|
-
|
|
396
|
+
Native autolinking wiring is done in all six Expo canary apps
|
|
397
|
+
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
|
|
398
|
+
`examples/expo-solid`, `examples/expo-angular`) — iOS Podfile/`AppDelegate.swift` +
|
|
399
|
+
`NSFaceIDUsageDescription`, Android Gradle/`MainApplication.kt` + `USE_BIOMETRIC`, all six
|
|
400
|
+
confirmed present. The one remaining gap: this package isn't yet demoed in the plain, non-Expo
|
|
401
|
+
`examples/react`/`vue-sfc`/`vue-tsx`/`svelte`/`solid`/`angular` canaries, since an
|
|
402
|
+
`expo-modules-core` package needs the `expo-modules-autolinking` wiring only the
|
|
403
|
+
`examples/expo-*` apps have set up so far.
|
|
@@ -55,7 +55,8 @@ export async function authenticateAsync(options = {}) {
|
|
|
55
55
|
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'authenticateAsync');
|
|
56
56
|
}
|
|
57
57
|
if (options.promptMessage !== undefined) {
|
|
58
|
-
invariant(typeof options.promptMessage === 'string' &&
|
|
58
|
+
invariant(typeof options.promptMessage === 'string' &&
|
|
59
|
+
options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
|
|
59
60
|
}
|
|
60
61
|
return expoLocalAuthentication.authenticateAsync({
|
|
61
62
|
...options,
|
|
@@ -55,7 +55,8 @@ export async function authenticateAsync(options = {}) {
|
|
|
55
55
|
throw new UnavailabilityError(NATIVE_MODULE_NAME, 'authenticateAsync');
|
|
56
56
|
}
|
|
57
57
|
if (options.promptMessage !== undefined) {
|
|
58
|
-
invariant(typeof options.promptMessage === 'string' &&
|
|
58
|
+
invariant(typeof options.promptMessage === 'string' &&
|
|
59
|
+
options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
|
|
59
60
|
}
|
|
60
61
|
return expoLocalAuthentication.authenticateAsync({
|
|
61
62
|
...options,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-authentication.js","sourceRoot":"","sources":["../../src/core/local-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAQ1D,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AACvD,MAAM,sBAAsB,GAAG,cAAc,CAAC;AAC9C,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAEtC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;
|
|
1
|
+
{"version":3,"file":"local-authentication.js","sourceRoot":"","sources":["../../src/core/local-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAQ1D,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AACvD,MAAM,sBAAsB,GAAG,cAAc,CAAC;AAC9C,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAEtC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IAGrD,IAAI,CAAC,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;QAC/D,MAAM,IAAI,mBAAmB,CAC3B,kBAAkB,EAClB,mCAAmC,CACpC,CAAC;IACJ,CAAC;IACD,OAAO,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC,uBAAuB,CAAC,eAAe,EAAE,CAAC;QAC7C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,uBAAuB,CAAC,eAAe,EAAE,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;QACnD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,uBAAuB,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAuC,EAAE;IAEzC,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,EAAE,CAAC;QAC/C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,SAAS,CACP,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ;YACvC,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAClC,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IAED,OAAO,uBAAuB,CAAC,iBAAiB,CAAC;QAC/C,GAAG,OAAO;QACV,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,sBAAsB;QAC9D,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,oBAAoB;KACzD,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;QAChD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;AACrD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,qCAAqC,GAAG,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,qCAAqC,GAAG,yBAAyB,CAAC;AAmBxE,MAAM,CAAC,MAAM,uBAAuB,GAClC,mBAAmB,CACjB,qCAAqC,CACtC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/local-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "expo-local-authentication wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android.",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "expo-local-authentication wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -13,6 +13,18 @@
|
|
|
13
13
|
"url": "https://github.com/OneEyed1366/symbiote-native/issues"
|
|
14
14
|
},
|
|
15
15
|
"author": "Andrey Prokopenko <psevdoproger@gmail.com>",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"react-native",
|
|
18
|
+
"symbiote-native",
|
|
19
|
+
"expo-local-authentication",
|
|
20
|
+
"react",
|
|
21
|
+
"vue",
|
|
22
|
+
"svelte",
|
|
23
|
+
"solid",
|
|
24
|
+
"angular",
|
|
25
|
+
"biometrics",
|
|
26
|
+
"face-id"
|
|
27
|
+
],
|
|
16
28
|
"type": "module",
|
|
17
29
|
"main": "./build/core/index.js",
|
|
18
30
|
"module": "./build/core/index.js",
|
|
@@ -23,16 +35,20 @@
|
|
|
23
35
|
"default": "./build/core/index.js"
|
|
24
36
|
},
|
|
25
37
|
"./vue": {
|
|
26
|
-
"types": "./build/
|
|
27
|
-
"default": "./build/
|
|
38
|
+
"types": "./build/core/index.d.ts",
|
|
39
|
+
"default": "./build/core/index.js"
|
|
28
40
|
},
|
|
29
41
|
"./react": {
|
|
30
|
-
"types": "./build/
|
|
31
|
-
"default": "./build/
|
|
42
|
+
"types": "./build/core/index.d.ts",
|
|
43
|
+
"default": "./build/core/index.js"
|
|
32
44
|
},
|
|
33
45
|
"./svelte": {
|
|
34
|
-
"types": "./build/
|
|
35
|
-
"default": "./build/
|
|
46
|
+
"types": "./build/core/index.d.ts",
|
|
47
|
+
"default": "./build/core/index.js"
|
|
48
|
+
},
|
|
49
|
+
"./solid": {
|
|
50
|
+
"types": "./build/core/index.d.ts",
|
|
51
|
+
"default": "./build/core/index.js"
|
|
36
52
|
},
|
|
37
53
|
"./angular": {
|
|
38
54
|
"types": "./build-ngc/angular/index.d.ts",
|
|
@@ -58,17 +74,19 @@
|
|
|
58
74
|
"invariant": "^2.2.4"
|
|
59
75
|
},
|
|
60
76
|
"peerDependencies": {
|
|
61
|
-
"@symbiote-native/angular": ">=0.6.2",
|
|
62
|
-
"@symbiote-native/engine": ">=0.1.7",
|
|
63
|
-
"@symbiote-native/react": ">=0.2.8",
|
|
64
|
-
"@symbiote-native/svelte": ">=0.1.0",
|
|
65
|
-
"@symbiote-native/vue": ">=0.3.8",
|
|
66
77
|
"@angular/core": ">=20",
|
|
67
78
|
"@vue/runtime-core": "^3.5.13",
|
|
68
79
|
"react": ">=19.0.0",
|
|
69
80
|
"react-native": ">=0.86",
|
|
81
|
+
"solid-js": ">=1.9.0",
|
|
70
82
|
"svelte": ">=5.56.0",
|
|
71
|
-
"vue": ">=3.5.0"
|
|
83
|
+
"vue": ">=3.5.0",
|
|
84
|
+
"@symbiote-native/angular": "^1.0.0",
|
|
85
|
+
"@symbiote-native/engine": "^0.4.0",
|
|
86
|
+
"@symbiote-native/react": "^1.0.0",
|
|
87
|
+
"@symbiote-native/solid": "^1.0.0",
|
|
88
|
+
"@symbiote-native/svelte": "^1.0.0",
|
|
89
|
+
"@symbiote-native/vue": "^1.0.0"
|
|
72
90
|
},
|
|
73
91
|
"peerDependenciesMeta": {
|
|
74
92
|
"@symbiote-native/angular": {
|
|
@@ -77,6 +95,9 @@
|
|
|
77
95
|
"@symbiote-native/react": {
|
|
78
96
|
"optional": true
|
|
79
97
|
},
|
|
98
|
+
"@symbiote-native/solid": {
|
|
99
|
+
"optional": true
|
|
100
|
+
},
|
|
80
101
|
"@symbiote-native/svelte": {
|
|
81
102
|
"optional": true
|
|
82
103
|
},
|
|
@@ -92,6 +113,9 @@
|
|
|
92
113
|
"react": {
|
|
93
114
|
"optional": true
|
|
94
115
|
},
|
|
116
|
+
"solid-js": {
|
|
117
|
+
"optional": true
|
|
118
|
+
},
|
|
95
119
|
"svelte": {
|
|
96
120
|
"optional": true
|
|
97
121
|
},
|
|
@@ -108,14 +132,16 @@
|
|
|
108
132
|
"@types/react": "^19.2.0",
|
|
109
133
|
"@vue/runtime-core": "^3.5.13",
|
|
110
134
|
"react": "19.2.3",
|
|
135
|
+
"solid-js": "^1.9.14",
|
|
111
136
|
"svelte": "^5.56.0",
|
|
112
137
|
"typescript": "~6.0.0",
|
|
113
|
-
"@symbiote-native/angular": "0.
|
|
114
|
-
"@symbiote-native/engine": "0.
|
|
115
|
-
"@symbiote-native/react": "0.
|
|
116
|
-
"@symbiote-native/
|
|
117
|
-
"@symbiote-native/
|
|
118
|
-
"@symbiote-native/
|
|
138
|
+
"@symbiote-native/angular": "1.0.0",
|
|
139
|
+
"@symbiote-native/engine": "0.4.0",
|
|
140
|
+
"@symbiote-native/react": "1.0.0",
|
|
141
|
+
"@symbiote-native/solid": "1.0.0",
|
|
142
|
+
"@symbiote-native/svelte": "1.0.0",
|
|
143
|
+
"@symbiote-native/test-utils": "0.3.0",
|
|
144
|
+
"@symbiote-native/vue": "1.0.0"
|
|
119
145
|
},
|
|
120
146
|
"scripts": {
|
|
121
147
|
"typecheck": "tsc --build",
|
|
@@ -28,9 +28,14 @@ export async function hasHardwareAsync(): Promise<boolean> {
|
|
|
28
28
|
* multiple authentication methods — e.g. `[FINGERPRINT, FACIAL_RECOGNITION]` means the device
|
|
29
29
|
* supports both. Returns an empty array if none are supported.
|
|
30
30
|
*/
|
|
31
|
-
export async function supportedAuthenticationTypesAsync(): Promise<
|
|
31
|
+
export async function supportedAuthenticationTypesAsync(): Promise<
|
|
32
|
+
AuthenticationType[]
|
|
33
|
+
> {
|
|
32
34
|
if (!expoLocalAuthentication.supportedAuthenticationTypesAsync) {
|
|
33
|
-
throw new UnavailabilityError(
|
|
35
|
+
throw new UnavailabilityError(
|
|
36
|
+
NATIVE_MODULE_NAME,
|
|
37
|
+
'supportedAuthenticationTypesAsync',
|
|
38
|
+
);
|
|
34
39
|
}
|
|
35
40
|
return expoLocalAuthentication.supportedAuthenticationTypesAsync();
|
|
36
41
|
}
|
|
@@ -72,7 +77,8 @@ export async function authenticateAsync(
|
|
|
72
77
|
|
|
73
78
|
if (options.promptMessage !== undefined) {
|
|
74
79
|
invariant(
|
|
75
|
-
typeof options.promptMessage === 'string' &&
|
|
80
|
+
typeof options.promptMessage === 'string' &&
|
|
81
|
+
options.promptMessage.length > 0,
|
|
76
82
|
'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.',
|
|
77
83
|
);
|
|
78
84
|
}
|
|
@@ -17,11 +17,15 @@ export type INativeLocalAuthenticationModule = {
|
|
|
17
17
|
isEnrolledAsync?(): Promise<boolean>;
|
|
18
18
|
getEnrolledLevelAsync?(): Promise<SecurityLevel>;
|
|
19
19
|
authenticateAsync?(
|
|
20
|
-
options: ILocalAuthenticationOptions & {
|
|
20
|
+
options: ILocalAuthenticationOptions & {
|
|
21
|
+
promptMessage: string;
|
|
22
|
+
cancelLabel: string;
|
|
23
|
+
},
|
|
21
24
|
): Promise<ILocalAuthenticationResult>;
|
|
22
25
|
cancelAuthenticate?(): Promise<void>;
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
export const expoLocalAuthentication =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
export const expoLocalAuthentication =
|
|
29
|
+
requireNativeModule<INativeLocalAuthenticationModule>(
|
|
30
|
+
EXPO_LOCAL_AUTHENTICATION_MODULE_NAME,
|
|
31
|
+
);
|
package/build/react/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../core';
|
package/build/react/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// @symbiote-native/local-auth/react: the React entry over the framework-agnostic core.
|
|
2
|
-
// Upstream ships free async functions and two enums, no per-instance state and no event
|
|
3
|
-
// stream (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a hook
|
|
4
|
-
// to wrap, so this is a plain re-export, mirroring how Pedometer's free functions pass through
|
|
5
|
-
// packages/sensors/src/react/index.ts untouched.
|
|
6
|
-
export * from '../core/index.js';
|
package/build/svelte/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../core';
|
package/build/svelte/index.js
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// @symbiote-native/local-auth/svelte: the Svelte entry over the framework-agnostic core.
|
|
2
|
-
// Upstream ships free async functions and two enums, no per-instance state and no event stream
|
|
3
|
-
// (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a rune to wrap,
|
|
4
|
-
// so this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes
|
|
5
|
-
// — never React's `hooks` or Vue's `composables`), and this package has none to fill.
|
|
6
|
-
export * from '../core/index.js';
|
package/build/vue/index.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from '../core';
|
package/build/vue/index.js
DELETED
package/src/react/index.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// @symbiote-native/local-auth/react: the React entry over the framework-agnostic core.
|
|
2
|
-
// Upstream ships free async functions and two enums, no per-instance state and no event
|
|
3
|
-
// stream (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a hook
|
|
4
|
-
// to wrap, so this is a plain re-export, mirroring how Pedometer's free functions pass through
|
|
5
|
-
// packages/sensors/src/react/index.ts untouched.
|
|
6
|
-
export * from '../core';
|
package/src/svelte/index.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// @symbiote-native/local-auth/svelte: the Svelte entry over the framework-agnostic core.
|
|
2
|
-
// Upstream ships free async functions and two enums, no per-instance state and no event stream
|
|
3
|
-
// (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a rune to wrap,
|
|
4
|
-
// so this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes
|
|
5
|
-
// — never React's `hooks` or Vue's `composables`), and this package has none to fill.
|
|
6
|
-
export * from '../core';
|
package/src/vue/index.ts
DELETED