@symbiote-native/local-auth 0.4.0 → 2.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 +167 -58
- package/package.json +27 -15
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
|
|
|
@@ -55,16 +55,17 @@ src/core/ hasHardwareAsync / isEnrolledAsync / getEnrolledLevelAsync /
|
|
|
55
55
|
src/angular/ @symbiote-native/local-auth/angular — export * from '../core'
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
`./react`, `./vue`, and `./
|
|
59
|
-
physical per-framework file, since there's nothing to subscribe to or clean up.
|
|
60
|
-
a physical file/subpath since Angular ships through a separate `ngc`/AOT build
|
|
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/`).
|
|
61
62
|
|
|
62
63
|
## Use it
|
|
63
64
|
|
|
64
65
|
```tsx
|
|
65
66
|
// React
|
|
66
67
|
import { useEffect, useState } from 'react';
|
|
67
|
-
import { Platform
|
|
68
|
+
import { Platform } from '@symbiote-native/react';
|
|
68
69
|
import {
|
|
69
70
|
authenticateAsync,
|
|
70
71
|
cancelAuthenticate,
|
|
@@ -91,26 +92,26 @@ function LocalAuthScreen() {
|
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
return (
|
|
94
|
-
<
|
|
95
|
-
<
|
|
95
|
+
<view>
|
|
96
|
+
<text>
|
|
96
97
|
{hasHardware && isEnrolled
|
|
97
98
|
? 'Ready to authenticate'
|
|
98
99
|
: 'No biometrics enrolled'}
|
|
99
|
-
</
|
|
100
|
-
<
|
|
101
|
-
<
|
|
102
|
-
</
|
|
100
|
+
</text>
|
|
101
|
+
<pressable onPress={handleAuthenticate}>
|
|
102
|
+
<text>Authenticate</text>
|
|
103
|
+
</pressable>
|
|
103
104
|
{Platform.OS === 'android' && (
|
|
104
|
-
<
|
|
105
|
-
<
|
|
106
|
-
</
|
|
105
|
+
<pressable onPress={() => cancelAuthenticate()}>
|
|
106
|
+
<text>Cancel</text>
|
|
107
|
+
</pressable>
|
|
107
108
|
)}
|
|
108
109
|
{authResult && (
|
|
109
|
-
<
|
|
110
|
+
<text>
|
|
110
111
|
{authResult.success ? 'Success' : `Failed: ${authResult.error}`}
|
|
111
|
-
</
|
|
112
|
+
</text>
|
|
112
113
|
)}
|
|
113
|
-
</
|
|
114
|
+
</view>
|
|
114
115
|
);
|
|
115
116
|
}
|
|
116
117
|
```
|
|
@@ -119,7 +120,7 @@ function LocalAuthScreen() {
|
|
|
119
120
|
<!-- Vue -->
|
|
120
121
|
<script setup lang="ts">
|
|
121
122
|
import { onMounted, ref } from 'vue';
|
|
122
|
-
import { Platform
|
|
123
|
+
import { Platform } from '@symbiote-native/vue';
|
|
123
124
|
import {
|
|
124
125
|
authenticateAsync,
|
|
125
126
|
cancelAuthenticate,
|
|
@@ -145,29 +146,132 @@ function handleAuthenticate(): void {
|
|
|
145
146
|
</script>
|
|
146
147
|
|
|
147
148
|
<template>
|
|
148
|
-
<
|
|
149
|
-
<
|
|
149
|
+
<view>
|
|
150
|
+
<text>{{
|
|
150
151
|
hasHardware && isEnrolled
|
|
151
152
|
? 'Ready to authenticate'
|
|
152
153
|
: 'No biometrics enrolled'
|
|
153
|
-
}}</
|
|
154
|
-
<
|
|
155
|
-
<
|
|
156
|
-
</
|
|
157
|
-
<
|
|
158
|
-
<
|
|
159
|
-
</
|
|
160
|
-
<
|
|
154
|
+
}}</text>
|
|
155
|
+
<pressable @press="handleAuthenticate">
|
|
156
|
+
<text>Authenticate</text>
|
|
157
|
+
</pressable>
|
|
158
|
+
<pressable v-if="Platform.OS === 'android'" @press="cancelAuthenticate">
|
|
159
|
+
<text>Cancel</text>
|
|
160
|
+
</pressable>
|
|
161
|
+
<text v-if="authResult">{{
|
|
161
162
|
authResult.success ? 'Success' : `Failed: ${authResult.error}`
|
|
162
|
-
}}</
|
|
163
|
-
</
|
|
163
|
+
}}</text>
|
|
164
|
+
</view>
|
|
164
165
|
</template>
|
|
165
166
|
```
|
|
166
167
|
|
|
168
|
+
```svelte
|
|
169
|
+
<!-- Svelte -->
|
|
170
|
+
<script lang="ts">
|
|
171
|
+
import { Platform } 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
|
|
199
|
+
? 'Ready to authenticate'
|
|
200
|
+
: 'No biometrics enrolled'}
|
|
201
|
+
</text>
|
|
202
|
+
<pressable onPress={handleAuthenticate}>
|
|
203
|
+
<text>Authenticate</text>
|
|
204
|
+
</pressable>
|
|
205
|
+
{#if Platform.OS === 'android'}
|
|
206
|
+
<pressable onPress={() => cancelAuthenticate()}>
|
|
207
|
+
<text>Cancel</text>
|
|
208
|
+
</pressable>
|
|
209
|
+
{/if}
|
|
210
|
+
{#if authResult}
|
|
211
|
+
<text>{authResult.success ? 'Success' : `Failed: ${authResult.error}`}</text
|
|
212
|
+
>
|
|
213
|
+
{/if}
|
|
214
|
+
</view>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```tsx
|
|
218
|
+
// Solid
|
|
219
|
+
import { Show, createSignal, onMount } from 'solid-js';
|
|
220
|
+
import { Platform } from '@symbiote-native/solid';
|
|
221
|
+
import {
|
|
222
|
+
authenticateAsync,
|
|
223
|
+
cancelAuthenticate,
|
|
224
|
+
hasHardwareAsync,
|
|
225
|
+
isEnrolledAsync,
|
|
226
|
+
} from '@symbiote-native/local-auth/solid';
|
|
227
|
+
import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/solid';
|
|
228
|
+
|
|
229
|
+
function LocalAuthScreen() {
|
|
230
|
+
const [hasHardware, setHasHardware] = createSignal(false);
|
|
231
|
+
const [isEnrolled, setIsEnrolled] = createSignal(false);
|
|
232
|
+
const [authResult, setAuthResult] =
|
|
233
|
+
createSignal<ILocalAuthenticationResult | null>(null);
|
|
234
|
+
|
|
235
|
+
onMount(() => {
|
|
236
|
+
void hasHardwareAsync().then(setHasHardware);
|
|
237
|
+
void isEnrolledAsync().then(setIsEnrolled);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const handleAuthenticate = () => {
|
|
241
|
+
void authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
|
|
242
|
+
setAuthResult,
|
|
243
|
+
);
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
return (
|
|
247
|
+
<view>
|
|
248
|
+
<text>
|
|
249
|
+
{hasHardware() && isEnrolled()
|
|
250
|
+
? 'Ready to authenticate'
|
|
251
|
+
: 'No biometrics enrolled'}
|
|
252
|
+
</text>
|
|
253
|
+
<pressable onPress={handleAuthenticate}>
|
|
254
|
+
<text>Authenticate</text>
|
|
255
|
+
</pressable>
|
|
256
|
+
{Platform.OS === 'android' && (
|
|
257
|
+
<pressable onPress={() => cancelAuthenticate()}>
|
|
258
|
+
<text>Cancel</text>
|
|
259
|
+
</pressable>
|
|
260
|
+
)}
|
|
261
|
+
<Show when={authResult()} keyed>
|
|
262
|
+
{result => (
|
|
263
|
+
<text>{result.success ? 'Success' : `Failed: ${result.error}`}</text>
|
|
264
|
+
)}
|
|
265
|
+
</Show>
|
|
266
|
+
</view>
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
167
271
|
```ts
|
|
168
272
|
// Angular
|
|
169
273
|
import { Component, signal } from '@angular/core';
|
|
170
|
-
import { Platform,
|
|
274
|
+
import { Platform, SYMBIOTE_ELEMENTS } from '@symbiote-native/angular';
|
|
171
275
|
import {
|
|
172
276
|
authenticateAsync,
|
|
173
277
|
cancelAuthenticate,
|
|
@@ -178,28 +282,28 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
|
|
|
178
282
|
|
|
179
283
|
@Component({
|
|
180
284
|
standalone: true,
|
|
181
|
-
imports: [
|
|
285
|
+
imports: [SYMBIOTE_ELEMENTS],
|
|
182
286
|
template: `
|
|
183
|
-
<
|
|
184
|
-
<
|
|
287
|
+
<view>
|
|
288
|
+
<text>{{
|
|
185
289
|
hasHardware() && isEnrolled()
|
|
186
290
|
? 'Ready to authenticate'
|
|
187
291
|
: 'No biometrics enrolled'
|
|
188
|
-
}}</
|
|
189
|
-
<
|
|
190
|
-
<
|
|
191
|
-
</
|
|
292
|
+
}}</text>
|
|
293
|
+
<pressable (press)="handleAuthenticate()">
|
|
294
|
+
<text>Authenticate</text>
|
|
295
|
+
</pressable>
|
|
192
296
|
@if (Platform.OS === 'android') {
|
|
193
|
-
<
|
|
194
|
-
<
|
|
195
|
-
</
|
|
297
|
+
<pressable (press)="handleCancel()">
|
|
298
|
+
<text>Cancel</text>
|
|
299
|
+
</pressable>
|
|
196
300
|
}
|
|
197
301
|
@if (authResult(); as result) {
|
|
198
|
-
<
|
|
302
|
+
<text>{{
|
|
199
303
|
result.success ? 'Success' : 'Failed: ' + result.error
|
|
200
|
-
}}</
|
|
304
|
+
}}</text>
|
|
201
305
|
}
|
|
202
|
-
</
|
|
306
|
+
</view>
|
|
203
307
|
`,
|
|
204
308
|
})
|
|
205
309
|
export class LocalAuthScreen {
|
|
@@ -226,17 +330,19 @@ export class LocalAuthScreen {
|
|
|
226
330
|
```
|
|
227
331
|
|
|
228
332
|
There's no per-instance service to `inject()` in the Angular case — every function is a plain
|
|
229
|
-
free function off the core package, called straight from the constructor.
|
|
230
|
-
trimmed from the real canary demo screens
|
|
333
|
+
free function off the core package, called straight from the constructor. The React/Vue/Angular
|
|
334
|
+
examples are trimmed from the real canary demo screens
|
|
335
|
+
(`examples/expo-react/screens/LocalAuthScreen.tsx`,
|
|
231
336
|
`examples/expo-vue-sfc/screens/LocalAuthScreen.vue`, `examples/expo-vue-tsx/screens/LocalAuthScreen.tsx`,
|
|
232
337
|
`examples/expo-angular/src/screens/LocalAuthScreen.ts`), which also cover
|
|
233
|
-
`getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card.
|
|
338
|
+
`getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card. No
|
|
339
|
+
canary demo screen exists yet for Svelte/Solid — the two snippets above follow the same surface.
|
|
234
340
|
|
|
235
341
|
## API
|
|
236
342
|
|
|
237
343
|
Free functions, no event stream, no per-instance state — upstream ships a handful of async
|
|
238
|
-
functions and two enums, not a subscribable sensor, so the React/Vue/Angular entry
|
|
239
|
-
are plain re-exports of `core` with nothing adapter-specific to add.
|
|
344
|
+
functions and two enums, not a subscribable sensor, so the React/Vue/Svelte/Solid/Angular entry
|
|
345
|
+
points above are plain re-exports of `core` with nothing adapter-specific to add.
|
|
240
346
|
|
|
241
347
|
```ts
|
|
242
348
|
hasHardwareAsync(): Promise<boolean>
|
|
@@ -260,6 +366,8 @@ import {
|
|
|
260
366
|
// or the framework-scoped entry points — identical surface, re-exported verbatim:
|
|
261
367
|
import { authenticateAsync } from '@symbiote-native/local-auth/react';
|
|
262
368
|
import { authenticateAsync } from '@symbiote-native/local-auth/vue';
|
|
369
|
+
import { authenticateAsync } from '@symbiote-native/local-auth/svelte';
|
|
370
|
+
import { authenticateAsync } from '@symbiote-native/local-auth/solid';
|
|
263
371
|
import { authenticateAsync } from '@symbiote-native/local-auth/angular';
|
|
264
372
|
```
|
|
265
373
|
|
|
@@ -286,10 +394,11 @@ a view or per-instance state. Tests inject a fake native-module object in place
|
|
|
286
394
|
`src/core/types.test.ts`, `vitest`) — no `installFabric()`, no ViewConfig. Native rendering itself
|
|
287
395
|
is verified on-device (see the parent [README](../../README.md) for the project's testing model).
|
|
288
396
|
|
|
289
|
-
Native autolinking wiring is done in
|
|
290
|
-
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-
|
|
291
|
-
— iOS Podfile/`AppDelegate.swift` +
|
|
292
|
-
`MainApplication.kt` + `USE_BIOMETRIC`, all
|
|
293
|
-
package isn't yet demoed in the plain, non-Expo
|
|
294
|
-
|
|
295
|
-
|
|
397
|
+
Native autolinking wiring is done in all six Expo canary apps
|
|
398
|
+
(`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
|
|
399
|
+
`examples/expo-solid`, `examples/expo-angular`) — iOS Podfile/`AppDelegate.swift` +
|
|
400
|
+
`NSFaceIDUsageDescription`, Android Gradle/`MainApplication.kt` + `USE_BIOMETRIC`, all six
|
|
401
|
+
confirmed present. The one remaining gap: this package isn't yet demoed in the plain, non-Expo
|
|
402
|
+
`examples/react`/`vue-sfc`/`vue-tsx`/`svelte`/`solid`/`angular` canaries, since an
|
|
403
|
+
`expo-modules-core` package needs the `expo-modules-autolinking` wiring only the
|
|
404
|
+
`examples/expo-*` apps have set up so far.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/local-auth",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
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": {
|
|
@@ -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",
|
|
@@ -62,19 +74,19 @@
|
|
|
62
74
|
"invariant": "^2.2.4"
|
|
63
75
|
},
|
|
64
76
|
"peerDependencies": {
|
|
65
|
-
"@symbiote-native/angular": ">=0.6.2",
|
|
66
|
-
"@symbiote-native/engine": ">=0.1.7",
|
|
67
|
-
"@symbiote-native/react": ">=0.2.8",
|
|
68
|
-
"@symbiote-native/solid": ">=0.0.1",
|
|
69
|
-
"@symbiote-native/svelte": ">=0.1.0",
|
|
70
|
-
"@symbiote-native/vue": ">=0.3.8",
|
|
71
77
|
"@angular/core": ">=20",
|
|
72
78
|
"@vue/runtime-core": "^3.5.13",
|
|
73
79
|
"react": ">=19.0.0",
|
|
74
80
|
"react-native": ">=0.86",
|
|
75
81
|
"solid-js": ">=1.9.0",
|
|
76
82
|
"svelte": ">=5.56.0",
|
|
77
|
-
"vue": ">=3.5.0"
|
|
83
|
+
"vue": ">=3.5.0",
|
|
84
|
+
"@symbiote-native/angular": "^2.0.0",
|
|
85
|
+
"@symbiote-native/engine": "^0.5.0",
|
|
86
|
+
"@symbiote-native/react": "^2.0.0",
|
|
87
|
+
"@symbiote-native/solid": "^2.0.0",
|
|
88
|
+
"@symbiote-native/svelte": "^2.0.0",
|
|
89
|
+
"@symbiote-native/vue": "^2.0.0"
|
|
78
90
|
},
|
|
79
91
|
"peerDependenciesMeta": {
|
|
80
92
|
"@symbiote-native/angular": {
|
|
@@ -123,13 +135,13 @@
|
|
|
123
135
|
"solid-js": "^1.9.14",
|
|
124
136
|
"svelte": "^5.56.0",
|
|
125
137
|
"typescript": "~6.0.0",
|
|
126
|
-
"@symbiote-native/angular": "0.
|
|
127
|
-
"@symbiote-native/engine": "0.
|
|
128
|
-
"@symbiote-native/react": "0.
|
|
129
|
-
"@symbiote-native/solid": "0.
|
|
130
|
-
"@symbiote-native/svelte": "0.
|
|
131
|
-
"@symbiote-native/test-utils": "0.
|
|
132
|
-
"@symbiote-native/vue": "0.
|
|
138
|
+
"@symbiote-native/angular": "2.0.0",
|
|
139
|
+
"@symbiote-native/engine": "0.5.0",
|
|
140
|
+
"@symbiote-native/react": "2.0.0",
|
|
141
|
+
"@symbiote-native/solid": "2.0.0",
|
|
142
|
+
"@symbiote-native/svelte": "2.0.0",
|
|
143
|
+
"@symbiote-native/test-utils": "0.3.0",
|
|
144
|
+
"@symbiote-native/vue": "2.0.0"
|
|
133
145
|
},
|
|
134
146
|
"scripts": {
|
|
135
147
|
"typecheck": "tsc --build",
|