@clerk/expo-biometrics 0.0.1

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +96 -0
  3. package/android/build.gradle +49 -0
  4. package/android/src/main/AndroidManifest.xml +2 -0
  5. package/android/src/main/java/expo/modules/clerk/biometrics/BiometricCredentialCoding.kt +147 -0
  6. package/android/src/main/java/expo/modules/clerk/biometrics/BiometricCredentialFileStore.kt +304 -0
  7. package/android/src/main/java/expo/modules/clerk/biometrics/BiometricCredentialStore.kt +96 -0
  8. package/android/src/main/java/expo/modules/clerk/biometrics/BiometricKeyManager.kt +216 -0
  9. package/android/src/main/java/expo/modules/clerk/biometrics/BiometricsError.kt +58 -0
  10. package/android/src/main/java/expo/modules/clerk/biometrics/ClerkExpoBiometricsModule.kt +139 -0
  11. package/android/src/test/java/expo/modules/clerk/biometrics/BiometricCredentialStorageContractTest.kt +450 -0
  12. package/android/src/test/java/expo/modules/clerk/biometrics/BiometricCredentialStoreTest.kt +179 -0
  13. package/android/src/test/java/expo/modules/clerk/biometrics/BiometricKeyManagerTest.kt +49 -0
  14. package/android/src/test/resources/biometric-credential-storage/v2/biometric_credentials.v2.json +50 -0
  15. package/android/src/test/resources/robolectric.properties +1 -0
  16. package/dist/ClerkExpoBiometricsModule.d.ts +18 -0
  17. package/dist/ClerkExpoBiometricsModule.d.ts.map +1 -0
  18. package/dist/ClerkExpoBiometricsModule.js +26 -0
  19. package/dist/ClerkExpoBiometricsModule.js.map +1 -0
  20. package/dist/errors.d.ts +11 -0
  21. package/dist/errors.d.ts.map +1 -0
  22. package/dist/errors.js +75 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/index.d.ts +57 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +204 -0
  27. package/dist/index.js.map +1 -0
  28. package/dist/types.d.ts +85 -0
  29. package/dist/types.d.ts.map +1 -0
  30. package/dist/types.js +17 -0
  31. package/dist/types.js.map +1 -0
  32. package/expo-module.config.json +9 -0
  33. package/ios/BiometricCredentialCoding.swift +335 -0
  34. package/ios/BiometricCredentialStore.swift +230 -0
  35. package/ios/BiometricKeyManager.swift +228 -0
  36. package/ios/BiometricsError.swift +117 -0
  37. package/ios/ClerkExpoBiometrics.podspec +32 -0
  38. package/ios/ClerkExpoBiometricsModule.swift +164 -0
  39. package/ios/Tests/BiometricCredentialContractTests.swift +286 -0
  40. package/ios/Tests/BiometricCredentialStoreTests.swift +323 -0
  41. package/ios/Tests/BiometricSystemIntegrationTests.swift +169 -0
  42. package/ios/Tests/Resources/BiometricCredentialStorageContractV1.json +31 -0
  43. package/package.json +53 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "version": 2,
3
+ "credentials": [
4
+ {
5
+ "id": "td_current_set",
6
+ "local_key_id": "tdlk_0123456789abcdef0123456789abcdef",
7
+ "user_id": "user_1",
8
+ "app_identifier": "com.example.app",
9
+ "identifier_hint_sha256": "b4c9a289323b21a01c3e940f150eb9b8c542587f1abfd8f0e1cc1ffc5e475514",
10
+ "policy": "biometry_current_set",
11
+ "created_at": 1735689600000,
12
+ "updated_at": 1735689600001,
13
+ "future_field": { "nested": [1, 2, 3] }
14
+ },
15
+ {
16
+ "id": "td_any",
17
+ "local_key_id": "tdlk_fedcba9876543210fedcba9876543210",
18
+ "user_id": "user_2",
19
+ "app_identifier": "com.example.app",
20
+ "policy": "biometry_any",
21
+ "created_at": 1735689700000,
22
+ "updated_at": 1735689700000
23
+ },
24
+ {
25
+ "id": "td_device_passcode",
26
+ "local_key_id": "tdlk_00000000000000000000000000000000",
27
+ "user_id": "user_3",
28
+ "app_identifier": "com.example.other",
29
+ "identifier_hint_sha256": null,
30
+ "policy": "biometry_or_device_passcode",
31
+ "created_at": 1735689800000,
32
+ "updated_at": 1735689800000
33
+ },
34
+ {
35
+ "id": "td_future_policy",
36
+ "local_key_id": "tdlk_11111111111111111111111111111111",
37
+ "user_id": "user_4",
38
+ "app_identifier": "com.example.app",
39
+ "policy": "some_future_policy",
40
+ "created_at": 1735689900000,
41
+ "updated_at": 1735689900000
42
+ },
43
+ {
44
+ "id": "td_incomplete",
45
+ "user_id": "user_5"
46
+ }
47
+ ],
48
+ "pending_cleanup_user_ids": ["user_6"],
49
+ "future_top_level_key": { "written_by": "a newer SDK" }
50
+ }
@@ -0,0 +1,18 @@
1
+ import type { BiometricAvailability, BiometricCredentialKey, BiometricCredentialPolicy, BiometricCredentialRecord, InstallationMarkerResult, SaveRecordOptions } from './types';
2
+ export interface ClerkExpoBiometricsNativeModule {
3
+ getAppIdentifier(): string;
4
+ hashIdentifierHint(hint: string): string | null;
5
+ getAvailability(): Promise<BiometricAvailability>;
6
+ createKey(policy: BiometricCredentialPolicy): Promise<BiometricCredentialKey>;
7
+ sign(localKeyId: string, clientData: string, reason: string | null): Promise<string>;
8
+ hasKey(localKeyId: string): Promise<boolean>;
9
+ deleteKey(localKeyId: string): Promise<void>;
10
+ /** JSON-encoded array of records, so fields the bridge cannot represent survive unchanged. */
11
+ listRecords(): Promise<string>;
12
+ saveRecord(record: BiometricCredentialRecord, options: SaveRecordOptions): Promise<void>;
13
+ deleteRecord(localKeyId: string): Promise<void>;
14
+ ensureInstallationMarker(): Promise<InstallationMarkerResult>;
15
+ }
16
+ declare const _default: ClerkExpoBiometricsNativeModule | null;
17
+ export default _default;
18
+ //# sourceMappingURL=ClerkExpoBiometricsModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClerkExpoBiometricsModule.d.ts","sourceRoot":"","sources":["../src/ClerkExpoBiometricsModule.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,+BAA+B;IAC9C,gBAAgB,IAAI,MAAM,CAAC;IAC3B,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChD,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAClD,SAAS,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAC9E,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrF,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,8FAA8F;IAC9F,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzF,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,wBAAwB,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC/D;;AAED,wBAAmG"}
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var ClerkExpoBiometricsModule_exports = {};
20
+ __export(ClerkExpoBiometricsModule_exports, {
21
+ default: () => ClerkExpoBiometricsModule_default
22
+ });
23
+ module.exports = __toCommonJS(ClerkExpoBiometricsModule_exports);
24
+ var import_expo = require("expo");
25
+ var ClerkExpoBiometricsModule_default = (0, import_expo.requireOptionalNativeModule)("ClerkExpoBiometrics");
26
+ //# sourceMappingURL=ClerkExpoBiometricsModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ClerkExpoBiometricsModule.ts"],"sourcesContent":["import { requireOptionalNativeModule } from 'expo';\n\nimport type {\n BiometricAvailability,\n BiometricCredentialKey,\n BiometricCredentialPolicy,\n BiometricCredentialRecord,\n InstallationMarkerResult,\n SaveRecordOptions,\n} from './types';\n\nexport interface ClerkExpoBiometricsNativeModule {\n getAppIdentifier(): string;\n hashIdentifierHint(hint: string): string | null;\n getAvailability(): Promise<BiometricAvailability>;\n createKey(policy: BiometricCredentialPolicy): Promise<BiometricCredentialKey>;\n sign(localKeyId: string, clientData: string, reason: string | null): Promise<string>;\n hasKey(localKeyId: string): Promise<boolean>;\n deleteKey(localKeyId: string): Promise<void>;\n /** JSON-encoded array of records, so fields the bridge cannot represent survive unchanged. */\n listRecords(): Promise<string>;\n saveRecord(record: BiometricCredentialRecord, options: SaveRecordOptions): Promise<void>;\n deleteRecord(localKeyId: string): Promise<void>;\n ensureInstallationMarker(): Promise<InstallationMarkerResult>;\n}\n\nexport default requireOptionalNativeModule<ClerkExpoBiometricsNativeModule>('ClerkExpoBiometrics');\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAA4C;AA0B5C,IAAO,wCAAQ,yCAA6D,qBAAqB;","names":[]}
@@ -0,0 +1,11 @@
1
+ import type { BiometricsErrorCode } from './types';
2
+ export declare class ClerkBiometricsError extends Error {
3
+ readonly code: BiometricsErrorCode;
4
+ constructor(code: BiometricsErrorCode, message: string, options?: {
5
+ cause?: unknown;
6
+ });
7
+ }
8
+ export declare function isClerkBiometricsError(error: unknown): error is ClerkBiometricsError;
9
+ export declare function isBiometricsErrorCode(value: unknown): value is BiometricsErrorCode;
10
+ export declare function toClerkBiometricsError(error: unknown): ClerkBiometricsError;
11
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAuBnD,qBAAa,oBAAqB,SAAQ,KAAK;IAC7C,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBAEvB,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAKtF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,oBAAoB,CAEpF;AAED,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAElF;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,oBAAoB,CAO3E"}
package/dist/errors.js ADDED
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var errors_exports = {};
20
+ __export(errors_exports, {
21
+ ClerkBiometricsError: () => ClerkBiometricsError,
22
+ isBiometricsErrorCode: () => isBiometricsErrorCode,
23
+ isClerkBiometricsError: () => isClerkBiometricsError,
24
+ toClerkBiometricsError: () => toClerkBiometricsError
25
+ });
26
+ module.exports = __toCommonJS(errors_exports);
27
+ const ERROR_CODES = /* @__PURE__ */ new Set([
28
+ "user_canceled",
29
+ "system_canceled",
30
+ "user_fallback",
31
+ "authentication_failed",
32
+ "biometry_not_available",
33
+ "biometry_not_enrolled",
34
+ "biometry_lockout",
35
+ "passcode_not_set",
36
+ "secure_key_storage_unavailable",
37
+ "key_not_found",
38
+ "key_invalidated",
39
+ "key_generation_failed",
40
+ "signing_failed",
41
+ "storage_failed",
42
+ "invalid_argument",
43
+ "not_implemented",
44
+ "native_module_unavailable",
45
+ "unknown"
46
+ ]);
47
+ class ClerkBiometricsError extends Error {
48
+ constructor(code, message, options) {
49
+ super(message, options);
50
+ this.name = "ClerkBiometricsError";
51
+ this.code = code;
52
+ }
53
+ }
54
+ function isClerkBiometricsError(error) {
55
+ return error instanceof ClerkBiometricsError;
56
+ }
57
+ function isBiometricsErrorCode(value) {
58
+ return typeof value === "string" && ERROR_CODES.has(value);
59
+ }
60
+ function toClerkBiometricsError(error) {
61
+ if (error instanceof ClerkBiometricsError) {
62
+ return error;
63
+ }
64
+ const code = error == null ? void 0 : error.code;
65
+ const message = error instanceof Error ? error.message : String(error);
66
+ return new ClerkBiometricsError(isBiometricsErrorCode(code) ? code : "unknown", message, { cause: error });
67
+ }
68
+ // Annotate the CommonJS export names for ESM import in node:
69
+ 0 && (module.exports = {
70
+ ClerkBiometricsError,
71
+ isBiometricsErrorCode,
72
+ isClerkBiometricsError,
73
+ toClerkBiometricsError
74
+ });
75
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts"],"sourcesContent":["import type { BiometricsErrorCode } from './types';\n\nconst ERROR_CODES: ReadonlySet<string> = new Set<BiometricsErrorCode>([\n 'user_canceled',\n 'system_canceled',\n 'user_fallback',\n 'authentication_failed',\n 'biometry_not_available',\n 'biometry_not_enrolled',\n 'biometry_lockout',\n 'passcode_not_set',\n 'secure_key_storage_unavailable',\n 'key_not_found',\n 'key_invalidated',\n 'key_generation_failed',\n 'signing_failed',\n 'storage_failed',\n 'invalid_argument',\n 'not_implemented',\n 'native_module_unavailable',\n 'unknown',\n]);\n\nexport class ClerkBiometricsError extends Error {\n readonly code: BiometricsErrorCode;\n\n constructor(code: BiometricsErrorCode, message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'ClerkBiometricsError';\n this.code = code;\n }\n}\n\nexport function isClerkBiometricsError(error: unknown): error is ClerkBiometricsError {\n return error instanceof ClerkBiometricsError;\n}\n\nexport function isBiometricsErrorCode(value: unknown): value is BiometricsErrorCode {\n return typeof value === 'string' && ERROR_CODES.has(value);\n}\n\nexport function toClerkBiometricsError(error: unknown): ClerkBiometricsError {\n if (error instanceof ClerkBiometricsError) {\n return error;\n }\n const code = (error as { code?: unknown } | null)?.code;\n const message = error instanceof Error ? error.message : String(error);\n return new ClerkBiometricsError(isBiometricsErrorCode(code) ? code : 'unknown', message, { cause: error });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,MAAM,cAAmC,oBAAI,IAAyB;AAAA,EACpE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,MAAM,6BAA6B,MAAM;AAAA,EAG9C,YAAY,MAA2B,SAAiB,SAA+B;AACrF,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd;AACF;AAEO,SAAS,uBAAuB,OAA+C;AACpF,SAAO,iBAAiB;AAC1B;AAEO,SAAS,sBAAsB,OAA8C;AAClF,SAAO,OAAO,UAAU,YAAY,YAAY,IAAI,KAAK;AAC3D;AAEO,SAAS,uBAAuB,OAAsC;AAC3E,MAAI,iBAAiB,sBAAsB;AACzC,WAAO;AAAA,EACT;AACA,QAAM,OAAQ,+BAAqC;AACnD,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,SAAO,IAAI,qBAAqB,sBAAsB,IAAI,IAAI,OAAO,WAAW,SAAS,EAAE,OAAO,MAAM,CAAC;AAC3G;","names":[]}
@@ -0,0 +1,57 @@
1
+ import type { BiometricAvailability, BiometricCredentialKey, BiometricCredentialPolicy, BiometricCredentialRecord, InstallationMarkerResult, SaveRecordOptions, StoredBiometricCredentialRecord } from './types';
2
+ export { ClerkBiometricsError, isBiometricsErrorCode, isClerkBiometricsError } from './errors';
3
+ export type { BiometricAvailability, BiometricCredentialKey, BiometricCredentialPolicy, BiometricCredentialRecord, BiometricsErrorCode, BiometryType, InstallationMarkerResult, SaveRecordOptions, StoredBiometricCredentialRecord, } from './types';
4
+ /**
5
+ * Returns the identifier Clerk uses as the credential's `app_identifier` (the iOS bundle identifier or the Android package name).
6
+ */
7
+ export declare function getAppIdentifier(): string;
8
+ /**
9
+ * Returns the SHA-256 of `hint` after trimming and lowercasing it, as 64 lowercase hex characters, or `null` when the
10
+ * normalized hint is empty. Compare it with a record's `identifierHintSha256` to match an identifier on every platform.
11
+ */
12
+ export declare function hashIdentifierHint(hint: string): string | null;
13
+ /**
14
+ * Reports which biometry the device supports and whether it can be used right now.
15
+ */
16
+ export declare function getAvailability(): Promise<BiometricAvailability>;
17
+ /**
18
+ * Creates a new hardware-backed P-256 private key protected by `policy`.
19
+ * Rejects with `secure_key_storage_unavailable` when the device has no hardware-backed key storage (see `getAvailability()`),
20
+ * and with `biometry_not_available`, `biometry_not_enrolled`, or `biometry_lockout` when biometrics cannot be used.
21
+ */
22
+ export declare function createKey(policy: BiometricCredentialPolicy): Promise<BiometricCredentialKey>;
23
+ /**
24
+ * Prompts for local authentication and signs the UTF-8 bytes of `clientData` with ES256.
25
+ * Resolves with the raw `r || s` signature, base64url-encoded without padding.
26
+ *
27
+ * @param reason - The message shown in the authentication prompt. On Android it is the prompt title, which defaults to the app name.
28
+ */
29
+ export declare function sign(localKeyId: string, clientData: string, reason?: string | null): Promise<string>;
30
+ /**
31
+ * Resolves whether the private key for `localKeyId` exists. Does not prompt for authentication.
32
+ */
33
+ export declare function hasKey(localKeyId: string): Promise<boolean>;
34
+ /**
35
+ * Deletes the private key for `localKeyId`. Resolves when the key does not exist.
36
+ */
37
+ export declare function deleteKey(localKeyId: string): Promise<void>;
38
+ /**
39
+ * Lists every well-formed credential record on the device, for every app identifier.
40
+ */
41
+ export declare function listRecords(): Promise<StoredBiometricCredentialRecord[]>;
42
+ /**
43
+ * Saves `record`, replacing any record with the same `id`. When the replaced record used a different key, that key is deleted.
44
+ */
45
+ export declare function saveRecord(record: BiometricCredentialRecord, options: SaveRecordOptions): Promise<void>;
46
+ /**
47
+ * Deletes the private key for `localKeyId`, then every record that references it.
48
+ * When the key cannot be deleted the records are kept and the promise rejects.
49
+ */
50
+ export declare function deleteRecord(localKeyId: string): Promise<void>;
51
+ /**
52
+ * Detects a new installation and deletes the records and keys a previous installation of this app left in the Keychain.
53
+ * Safe to call repeatedly. The store operations above call it before they read or write.
54
+ * On Android it always resolves `{ wiped: false }`: uninstalling the app deletes its records and keys.
55
+ */
56
+ export declare function ensureInstallationMarker(): Promise<InstallationMarkerResult>;
57
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,wBAAwB,EACxB,iBAAiB,EACjB,+BAA+B,EAChC,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAC/F,YAAY,EACV,qBAAqB,EACrB,sBAAsB,EACtB,yBAAyB,EACzB,yBAAyB,EACzB,mBAAmB,EACnB,YAAY,EACZ,wBAAwB,EACxB,iBAAiB,EACjB,+BAA+B,GAChC,MAAM,SAAS,CAAC;AA0EjB;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK9D;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAEhE;AAED;;;;GAIG;AACH,wBAAsB,SAAS,CAAC,MAAM,EAAE,yBAAyB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAGlG;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAMhH;AAED;;GAEG;AACH,wBAAsB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAc9E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,yBAAyB,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB7G;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpE;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAE5E"}
package/dist/index.js ADDED
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var index_exports = {};
30
+ __export(index_exports, {
31
+ ClerkBiometricsError: () => import_errors2.ClerkBiometricsError,
32
+ createKey: () => createKey,
33
+ deleteKey: () => deleteKey,
34
+ deleteRecord: () => deleteRecord,
35
+ ensureInstallationMarker: () => ensureInstallationMarker,
36
+ getAppIdentifier: () => getAppIdentifier,
37
+ getAvailability: () => getAvailability,
38
+ hasKey: () => hasKey,
39
+ hashIdentifierHint: () => hashIdentifierHint,
40
+ isBiometricsErrorCode: () => import_errors2.isBiometricsErrorCode,
41
+ isClerkBiometricsError: () => import_errors2.isClerkBiometricsError,
42
+ listRecords: () => listRecords,
43
+ saveRecord: () => saveRecord,
44
+ sign: () => sign
45
+ });
46
+ module.exports = __toCommonJS(index_exports);
47
+ var import_ClerkExpoBiometricsModule = __toESM(require("./ClerkExpoBiometricsModule"));
48
+ var import_errors = require("./errors");
49
+ var import_errors2 = require("./errors");
50
+ const POLICIES = /* @__PURE__ */ new Set([
51
+ "biometry_current_set",
52
+ "biometry_any",
53
+ "biometry_or_device_passcode"
54
+ ]);
55
+ function nativeModule() {
56
+ if (!import_ClerkExpoBiometricsModule.default) {
57
+ throw new import_errors.ClerkBiometricsError(
58
+ "native_module_unavailable",
59
+ "The ClerkExpoBiometrics native module is not available. Rebuild your app with @clerk/expo-biometrics installed; it cannot run in Expo Go or on the web."
60
+ );
61
+ }
62
+ return import_ClerkExpoBiometricsModule.default;
63
+ }
64
+ async function callNative(fn) {
65
+ const module2 = nativeModule();
66
+ try {
67
+ return await fn(module2);
68
+ } catch (error) {
69
+ throw (0, import_errors.toClerkBiometricsError)(error);
70
+ }
71
+ }
72
+ function invalidArgument(message) {
73
+ return new import_errors.ClerkBiometricsError("invalid_argument", message);
74
+ }
75
+ function assertNonEmptyString(value, name) {
76
+ if (typeof value !== "string" || value.length === 0) {
77
+ throw invalidArgument(`${name} must be a non-empty string.`);
78
+ }
79
+ }
80
+ function assertPolicy(value) {
81
+ if (typeof value !== "string" || !POLICIES.has(value)) {
82
+ throw invalidArgument(`policy must be one of ${[...POLICIES].join(", ")}.`);
83
+ }
84
+ }
85
+ function assertTimestamp(value, name) {
86
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
87
+ throw invalidArgument(`${name} must be a non-negative number of milliseconds since the Unix epoch.`);
88
+ }
89
+ }
90
+ function assertRecord(record) {
91
+ if (typeof record !== "object" || record === null) {
92
+ throw invalidArgument("record must be an object.");
93
+ }
94
+ assertNonEmptyString(record.id, "record.id");
95
+ assertNonEmptyString(record.localKeyId, "record.localKeyId");
96
+ assertNonEmptyString(record.userId, "record.userId");
97
+ assertNonEmptyString(record.appIdentifier, "record.appIdentifier");
98
+ if (record.identifierHint !== void 0 && typeof record.identifierHint !== "string") {
99
+ throw invalidArgument("record.identifierHint must be a string when provided.");
100
+ }
101
+ assertPolicy(record.policy);
102
+ assertTimestamp(record.createdAt, "record.createdAt");
103
+ assertTimestamp(record.updatedAt, "record.updatedAt");
104
+ }
105
+ function callNativeSync(fn) {
106
+ const module2 = nativeModule();
107
+ try {
108
+ return fn(module2);
109
+ } catch (error) {
110
+ throw (0, import_errors.toClerkBiometricsError)(error);
111
+ }
112
+ }
113
+ function getAppIdentifier() {
114
+ return callNativeSync((module2) => module2.getAppIdentifier());
115
+ }
116
+ function hashIdentifierHint(hint) {
117
+ if (typeof hint !== "string") {
118
+ throw invalidArgument("hint must be a string.");
119
+ }
120
+ return callNativeSync((module2) => module2.hashIdentifierHint(hint));
121
+ }
122
+ function getAvailability() {
123
+ return callNative((module2) => module2.getAvailability());
124
+ }
125
+ async function createKey(policy) {
126
+ assertPolicy(policy);
127
+ return callNative((module2) => module2.createKey(policy));
128
+ }
129
+ async function sign(localKeyId, clientData, reason = null) {
130
+ assertNonEmptyString(localKeyId, "localKeyId");
131
+ if (typeof clientData !== "string") {
132
+ throw invalidArgument("clientData must be a string.");
133
+ }
134
+ return callNative((module2) => module2.sign(localKeyId, clientData, reason));
135
+ }
136
+ async function hasKey(localKeyId) {
137
+ assertNonEmptyString(localKeyId, "localKeyId");
138
+ return callNative((module2) => module2.hasKey(localKeyId));
139
+ }
140
+ async function deleteKey(localKeyId) {
141
+ assertNonEmptyString(localKeyId, "localKeyId");
142
+ return callNative((module2) => module2.deleteKey(localKeyId));
143
+ }
144
+ async function listRecords() {
145
+ const json = await callNative((module2) => module2.listRecords());
146
+ let records;
147
+ try {
148
+ records = JSON.parse(json);
149
+ } catch (error) {
150
+ throw new import_errors.ClerkBiometricsError("storage_failed", "The native module returned invalid record data.", {
151
+ cause: error
152
+ });
153
+ }
154
+ if (!Array.isArray(records)) {
155
+ throw new import_errors.ClerkBiometricsError("storage_failed", "The native module returned invalid record data.");
156
+ }
157
+ return records;
158
+ }
159
+ async function saveRecord(record, options) {
160
+ assertRecord(record);
161
+ if (typeof (options == null ? void 0 : options.removeOtherRecordsForApp) !== "boolean") {
162
+ throw invalidArgument("options.removeOtherRecordsForApp must be a boolean.");
163
+ }
164
+ const nativeRecord = {
165
+ id: record.id,
166
+ localKeyId: record.localKeyId,
167
+ userId: record.userId,
168
+ appIdentifier: record.appIdentifier,
169
+ policy: record.policy,
170
+ createdAt: record.createdAt,
171
+ updatedAt: record.updatedAt
172
+ };
173
+ if (record.identifierHint !== void 0) {
174
+ nativeRecord.identifierHint = record.identifierHint;
175
+ }
176
+ return callNative(
177
+ (module2) => module2.saveRecord(nativeRecord, { removeOtherRecordsForApp: options.removeOtherRecordsForApp })
178
+ );
179
+ }
180
+ async function deleteRecord(localKeyId) {
181
+ assertNonEmptyString(localKeyId, "localKeyId");
182
+ return callNative((module2) => module2.deleteRecord(localKeyId));
183
+ }
184
+ function ensureInstallationMarker() {
185
+ return callNative((module2) => module2.ensureInstallationMarker());
186
+ }
187
+ // Annotate the CommonJS export names for ESM import in node:
188
+ 0 && (module.exports = {
189
+ ClerkBiometricsError,
190
+ createKey,
191
+ deleteKey,
192
+ deleteRecord,
193
+ ensureInstallationMarker,
194
+ getAppIdentifier,
195
+ getAvailability,
196
+ hasKey,
197
+ hashIdentifierHint,
198
+ isBiometricsErrorCode,
199
+ isClerkBiometricsError,
200
+ listRecords,
201
+ saveRecord,
202
+ sign
203
+ });
204
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import type { ClerkExpoBiometricsNativeModule } from './ClerkExpoBiometricsModule';\nimport ClerkExpoBiometrics from './ClerkExpoBiometricsModule';\nimport { ClerkBiometricsError, toClerkBiometricsError } from './errors';\nimport type {\n BiometricAvailability,\n BiometricCredentialKey,\n BiometricCredentialPolicy,\n BiometricCredentialRecord,\n InstallationMarkerResult,\n SaveRecordOptions,\n StoredBiometricCredentialRecord,\n} from './types';\n\nexport { ClerkBiometricsError, isBiometricsErrorCode, isClerkBiometricsError } from './errors';\nexport type {\n BiometricAvailability,\n BiometricCredentialKey,\n BiometricCredentialPolicy,\n BiometricCredentialRecord,\n BiometricsErrorCode,\n BiometryType,\n InstallationMarkerResult,\n SaveRecordOptions,\n StoredBiometricCredentialRecord,\n} from './types';\n\nconst POLICIES: ReadonlySet<string> = new Set<BiometricCredentialPolicy>([\n 'biometry_current_set',\n 'biometry_any',\n 'biometry_or_device_passcode',\n]);\n\nfunction nativeModule(): ClerkExpoBiometricsNativeModule {\n if (!ClerkExpoBiometrics) {\n throw new ClerkBiometricsError(\n 'native_module_unavailable',\n 'The ClerkExpoBiometrics native module is not available. Rebuild your app with @clerk/expo-biometrics installed; it cannot run in Expo Go or on the web.',\n );\n }\n return ClerkExpoBiometrics;\n}\n\nasync function callNative<T>(fn: (module: ClerkExpoBiometricsNativeModule) => Promise<T>): Promise<T> {\n const module = nativeModule();\n try {\n return await fn(module);\n } catch (error) {\n throw toClerkBiometricsError(error);\n }\n}\n\nfunction invalidArgument(message: string): ClerkBiometricsError {\n return new ClerkBiometricsError('invalid_argument', message);\n}\n\nfunction assertNonEmptyString(value: unknown, name: string): asserts value is string {\n if (typeof value !== 'string' || value.length === 0) {\n throw invalidArgument(`${name} must be a non-empty string.`);\n }\n}\n\nfunction assertPolicy(value: unknown): asserts value is BiometricCredentialPolicy {\n if (typeof value !== 'string' || !POLICIES.has(value)) {\n throw invalidArgument(`policy must be one of ${[...POLICIES].join(', ')}.`);\n }\n}\n\nfunction assertTimestamp(value: unknown, name: string): asserts value is number {\n if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {\n throw invalidArgument(`${name} must be a non-negative number of milliseconds since the Unix epoch.`);\n }\n}\n\nfunction assertRecord(record: BiometricCredentialRecord): void {\n if (typeof record !== 'object' || record === null) {\n throw invalidArgument('record must be an object.');\n }\n assertNonEmptyString(record.id, 'record.id');\n assertNonEmptyString(record.localKeyId, 'record.localKeyId');\n assertNonEmptyString(record.userId, 'record.userId');\n assertNonEmptyString(record.appIdentifier, 'record.appIdentifier');\n if (record.identifierHint !== undefined && typeof record.identifierHint !== 'string') {\n throw invalidArgument('record.identifierHint must be a string when provided.');\n }\n assertPolicy(record.policy);\n assertTimestamp(record.createdAt, 'record.createdAt');\n assertTimestamp(record.updatedAt, 'record.updatedAt');\n}\n\nfunction callNativeSync<T>(fn: (module: ClerkExpoBiometricsNativeModule) => T): T {\n const module = nativeModule();\n try {\n return fn(module);\n } catch (error) {\n throw toClerkBiometricsError(error);\n }\n}\n\n/**\n * Returns the identifier Clerk uses as the credential's `app_identifier` (the iOS bundle identifier or the Android package name).\n */\nexport function getAppIdentifier(): string {\n return callNativeSync(module => module.getAppIdentifier());\n}\n\n/**\n * Returns the SHA-256 of `hint` after trimming and lowercasing it, as 64 lowercase hex characters, or `null` when the\n * normalized hint is empty. Compare it with a record's `identifierHintSha256` to match an identifier on every platform.\n */\nexport function hashIdentifierHint(hint: string): string | null {\n if (typeof hint !== 'string') {\n throw invalidArgument('hint must be a string.');\n }\n return callNativeSync(module => module.hashIdentifierHint(hint));\n}\n\n/**\n * Reports which biometry the device supports and whether it can be used right now.\n */\nexport function getAvailability(): Promise<BiometricAvailability> {\n return callNative(module => module.getAvailability());\n}\n\n/**\n * Creates a new hardware-backed P-256 private key protected by `policy`.\n * Rejects with `secure_key_storage_unavailable` when the device has no hardware-backed key storage (see `getAvailability()`),\n * and with `biometry_not_available`, `biometry_not_enrolled`, or `biometry_lockout` when biometrics cannot be used.\n */\nexport async function createKey(policy: BiometricCredentialPolicy): Promise<BiometricCredentialKey> {\n assertPolicy(policy);\n return callNative(module => module.createKey(policy));\n}\n\n/**\n * Prompts for local authentication and signs the UTF-8 bytes of `clientData` with ES256.\n * Resolves with the raw `r || s` signature, base64url-encoded without padding.\n *\n * @param reason - The message shown in the authentication prompt. On Android it is the prompt title, which defaults to the app name.\n */\nexport async function sign(localKeyId: string, clientData: string, reason: string | null = null): Promise<string> {\n assertNonEmptyString(localKeyId, 'localKeyId');\n if (typeof clientData !== 'string') {\n throw invalidArgument('clientData must be a string.');\n }\n return callNative(module => module.sign(localKeyId, clientData, reason));\n}\n\n/**\n * Resolves whether the private key for `localKeyId` exists. Does not prompt for authentication.\n */\nexport async function hasKey(localKeyId: string): Promise<boolean> {\n assertNonEmptyString(localKeyId, 'localKeyId');\n return callNative(module => module.hasKey(localKeyId));\n}\n\n/**\n * Deletes the private key for `localKeyId`. Resolves when the key does not exist.\n */\nexport async function deleteKey(localKeyId: string): Promise<void> {\n assertNonEmptyString(localKeyId, 'localKeyId');\n return callNative(module => module.deleteKey(localKeyId));\n}\n\n/**\n * Lists every well-formed credential record on the device, for every app identifier.\n */\nexport async function listRecords(): Promise<StoredBiometricCredentialRecord[]> {\n const json = await callNative(module => module.listRecords());\n let records: unknown;\n try {\n records = JSON.parse(json);\n } catch (error) {\n throw new ClerkBiometricsError('storage_failed', 'The native module returned invalid record data.', {\n cause: error,\n });\n }\n if (!Array.isArray(records)) {\n throw new ClerkBiometricsError('storage_failed', 'The native module returned invalid record data.');\n }\n return records as StoredBiometricCredentialRecord[];\n}\n\n/**\n * Saves `record`, replacing any record with the same `id`. When the replaced record used a different key, that key is deleted.\n */\nexport async function saveRecord(record: BiometricCredentialRecord, options: SaveRecordOptions): Promise<void> {\n assertRecord(record);\n if (typeof options?.removeOtherRecordsForApp !== 'boolean') {\n throw invalidArgument('options.removeOtherRecordsForApp must be a boolean.');\n }\n const nativeRecord: BiometricCredentialRecord = {\n id: record.id,\n localKeyId: record.localKeyId,\n userId: record.userId,\n appIdentifier: record.appIdentifier,\n policy: record.policy,\n createdAt: record.createdAt,\n updatedAt: record.updatedAt,\n };\n if (record.identifierHint !== undefined) {\n nativeRecord.identifierHint = record.identifierHint;\n }\n return callNative(module =>\n module.saveRecord(nativeRecord, { removeOtherRecordsForApp: options.removeOtherRecordsForApp }),\n );\n}\n\n/**\n * Deletes the private key for `localKeyId`, then every record that references it.\n * When the key cannot be deleted the records are kept and the promise rejects.\n */\nexport async function deleteRecord(localKeyId: string): Promise<void> {\n assertNonEmptyString(localKeyId, 'localKeyId');\n return callNative(module => module.deleteRecord(localKeyId));\n}\n\n/**\n * Detects a new installation and deletes the records and keys a previous installation of this app left in the Keychain.\n * Safe to call repeatedly. The store operations above call it before they read or write.\n * On Android it always resolves `{ wiped: false }`: uninstalling the app deletes its records and keys.\n */\nexport function ensureInstallationMarker(): Promise<InstallationMarkerResult> {\n return callNative(module => module.ensureInstallationMarker());\n}\n"],"mappings":"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","names":["import_errors","ClerkExpoBiometrics","module"]}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The local authentication policy that protects a biometric credential's private key.
3
+ *
4
+ * - `biometry_current_set`: requires a biometric from the currently enrolled set. Adding or removing a Face ID / Touch ID enrollment invalidates the key.
5
+ * - `biometry_any`: requires a biometric, and survives biometric enrollment changes.
6
+ * - `biometry_or_device_passcode`: requires biometrics to be available at creation, then accepts a biometric or the device passcode.
7
+ */
8
+ export type BiometricCredentialPolicy = 'biometry_current_set' | 'biometry_any' | 'biometry_or_device_passcode';
9
+ /**
10
+ * The biometry the device supports. iOS reports `faceID`, `touchID`, or `opticID`. Android cannot tell which sensor is a
11
+ * strong (Class 3) biometric, so it reports `biometric` whenever one is present.
12
+ */
13
+ export type BiometryType = 'faceID' | 'touchID' | 'opticID' | 'biometric' | 'none';
14
+ export interface BiometricAvailability {
15
+ /** The biometry the device supports, or `none`. */
16
+ biometryType: BiometryType;
17
+ /** Whether biometric authentication can be evaluated now. Key creation requires this for every policy. */
18
+ canEvaluateBiometrics: boolean;
19
+ /** Whether device owner authentication (biometrics or passcode) can be evaluated now. */
20
+ canEvaluateDeviceOwner: boolean;
21
+ /** Why biometric authentication cannot be evaluated, or `null` when it can. */
22
+ errorCode: BiometricsErrorCode | null;
23
+ /**
24
+ * Whether the device has hardware-backed key storage: the Secure Enclave on iOS, which the iOS Simulator lacks, or the
25
+ * Android Keystore on Android 9 (API level 28) and later.
26
+ * `createKey()` rejects with `secure_key_storage_unavailable` when this is `false`.
27
+ */
28
+ secureKeyStorageAvailable: boolean;
29
+ }
30
+ export interface BiometricCredentialKey {
31
+ /** Opaque identifier of the private key. Stored in the credential record as `localKeyId`. */
32
+ localKeyId: string;
33
+ /** P-256 public key as a compact JWK string: `{"kty":"EC","crv":"P-256","x":"…","y":"…","alg":"ES256"}`. */
34
+ publicKeyJwk: string;
35
+ }
36
+ /**
37
+ * On-device metadata linking a Clerk biometric credential to its private key.
38
+ */
39
+ export interface BiometricCredentialRecord {
40
+ /** Server credential ID. */
41
+ id: string;
42
+ /** Identifier of the private key returned by `createKey()`. */
43
+ localKeyId: string;
44
+ /** Clerk user ID that enrolled the credential. */
45
+ userId: string;
46
+ /** App identifier the credential was enrolled for (see `getAppIdentifier()`). */
47
+ appIdentifier: string;
48
+ /**
49
+ * Local-only identifier hint. Normalized (trimmed, lowercased) when stored; empty values are omitted.
50
+ * iOS stores the normalized hint; Android stores only its hash (see `hashIdentifierHint()`).
51
+ */
52
+ identifierHint?: string;
53
+ policy: BiometricCredentialPolicy;
54
+ /** Server credential creation time, in milliseconds since the Unix epoch. */
55
+ createdAt: number;
56
+ /** Server credential update time, in milliseconds since the Unix epoch. */
57
+ updatedAt: number;
58
+ }
59
+ /**
60
+ * A record read from the store. Fields written by other SDK versions are passed through unchanged.
61
+ */
62
+ export type StoredBiometricCredentialRecord = Omit<BiometricCredentialRecord, 'identifierHint'> & {
63
+ /** The normalized identifier hint on iOS, when one was stored. Always `null` on Android, which stores only the hash. */
64
+ identifierHint?: string | null;
65
+ /** `hashIdentifierHint()` of the stored identifier hint, or `null` when there is none. Compare hints with this field. */
66
+ identifierHintSha256: string | null;
67
+ readonly [field: string]: unknown;
68
+ };
69
+ export interface SaveRecordOptions {
70
+ /**
71
+ * Delete every other record for the same `appIdentifier`, along with its private key, after the record is saved.
72
+ * Set this after a successful enrollment, since the server has already replaced those credentials.
73
+ * On Android only the same `userId`'s other records are deleted, as the shared storage contract requires.
74
+ */
75
+ removeOtherRecordsForApp: boolean;
76
+ }
77
+ export interface InstallationMarkerResult {
78
+ /**
79
+ * `true` when this is a new installation and the records left behind by a previous one were deleted.
80
+ * Always `false` on Android, where uninstalling the app already deletes its records and keys.
81
+ */
82
+ wiped: boolean;
83
+ }
84
+ export type BiometricsErrorCode = 'user_canceled' | 'system_canceled' | 'user_fallback' | 'authentication_failed' | 'biometry_not_available' | 'biometry_not_enrolled' | 'biometry_lockout' | 'passcode_not_set' | 'secure_key_storage_unavailable' | 'key_not_found' | 'key_invalidated' | 'key_generation_failed' | 'signing_failed' | 'storage_failed' | 'invalid_argument' | 'not_implemented' | 'native_module_unavailable' | 'unknown';
85
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,yBAAyB,GAAG,sBAAsB,GAAG,cAAc,GAAG,6BAA6B,CAAC;AAEhH;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,YAAY,EAAE,YAAY,CAAC;IAC3B,0GAA0G;IAC1G,qBAAqB,EAAE,OAAO,CAAC;IAC/B,yFAAyF;IACzF,sBAAsB,EAAE,OAAO,CAAC;IAChC,+EAA+E;IAC/E,SAAS,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACtC;;;;OAIG;IACH,yBAAyB,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,6FAA6F;IAC7F,UAAU,EAAE,MAAM,CAAC;IACnB,4GAA4G;IAC5G,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,4BAA4B;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,+DAA+D;IAC/D,UAAU,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,iFAAiF;IACjF,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,EAAE,yBAAyB,CAAC;IAClC,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,CAAC,GAAG;IAChG,wHAAwH;IACxH,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,yHAAyH;IACzH,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,wBAAwB,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,wBAAwB;IACvC;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,uBAAuB,GACvB,wBAAwB,GACxB,uBAAuB,GACvB,kBAAkB,GAClB,kBAAkB,GAClB,gCAAgC,GAChC,eAAe,GACf,iBAAiB,GACjB,uBAAuB,GACvB,gBAAgB,GAChB,gBAAgB,GAChB,kBAAkB,GAClB,iBAAiB,GACjB,2BAA2B,GAC3B,SAAS,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+ var types_exports = {};
16
+ module.exports = __toCommonJS(types_exports);
17
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * The local authentication policy that protects a biometric credential's private key.\n *\n * - `biometry_current_set`: requires a biometric from the currently enrolled set. Adding or removing a Face ID / Touch ID enrollment invalidates the key.\n * - `biometry_any`: requires a biometric, and survives biometric enrollment changes.\n * - `biometry_or_device_passcode`: requires biometrics to be available at creation, then accepts a biometric or the device passcode.\n */\nexport type BiometricCredentialPolicy = 'biometry_current_set' | 'biometry_any' | 'biometry_or_device_passcode';\n\n/**\n * The biometry the device supports. iOS reports `faceID`, `touchID`, or `opticID`. Android cannot tell which sensor is a\n * strong (Class 3) biometric, so it reports `biometric` whenever one is present.\n */\nexport type BiometryType = 'faceID' | 'touchID' | 'opticID' | 'biometric' | 'none';\n\nexport interface BiometricAvailability {\n /** The biometry the device supports, or `none`. */\n biometryType: BiometryType;\n /** Whether biometric authentication can be evaluated now. Key creation requires this for every policy. */\n canEvaluateBiometrics: boolean;\n /** Whether device owner authentication (biometrics or passcode) can be evaluated now. */\n canEvaluateDeviceOwner: boolean;\n /** Why biometric authentication cannot be evaluated, or `null` when it can. */\n errorCode: BiometricsErrorCode | null;\n /**\n * Whether the device has hardware-backed key storage: the Secure Enclave on iOS, which the iOS Simulator lacks, or the\n * Android Keystore on Android 9 (API level 28) and later.\n * `createKey()` rejects with `secure_key_storage_unavailable` when this is `false`.\n */\n secureKeyStorageAvailable: boolean;\n}\n\nexport interface BiometricCredentialKey {\n /** Opaque identifier of the private key. Stored in the credential record as `localKeyId`. */\n localKeyId: string;\n /** P-256 public key as a compact JWK string: `{\"kty\":\"EC\",\"crv\":\"P-256\",\"x\":\"…\",\"y\":\"…\",\"alg\":\"ES256\"}`. */\n publicKeyJwk: string;\n}\n\n/**\n * On-device metadata linking a Clerk biometric credential to its private key.\n */\nexport interface BiometricCredentialRecord {\n /** Server credential ID. */\n id: string;\n /** Identifier of the private key returned by `createKey()`. */\n localKeyId: string;\n /** Clerk user ID that enrolled the credential. */\n userId: string;\n /** App identifier the credential was enrolled for (see `getAppIdentifier()`). */\n appIdentifier: string;\n /**\n * Local-only identifier hint. Normalized (trimmed, lowercased) when stored; empty values are omitted.\n * iOS stores the normalized hint; Android stores only its hash (see `hashIdentifierHint()`).\n */\n identifierHint?: string;\n policy: BiometricCredentialPolicy;\n /** Server credential creation time, in milliseconds since the Unix epoch. */\n createdAt: number;\n /** Server credential update time, in milliseconds since the Unix epoch. */\n updatedAt: number;\n}\n\n/**\n * A record read from the store. Fields written by other SDK versions are passed through unchanged.\n */\nexport type StoredBiometricCredentialRecord = Omit<BiometricCredentialRecord, 'identifierHint'> & {\n /** The normalized identifier hint on iOS, when one was stored. Always `null` on Android, which stores only the hash. */\n identifierHint?: string | null;\n /** `hashIdentifierHint()` of the stored identifier hint, or `null` when there is none. Compare hints with this field. */\n identifierHintSha256: string | null;\n readonly [field: string]: unknown;\n};\n\nexport interface SaveRecordOptions {\n /**\n * Delete every other record for the same `appIdentifier`, along with its private key, after the record is saved.\n * Set this after a successful enrollment, since the server has already replaced those credentials.\n * On Android only the same `userId`'s other records are deleted, as the shared storage contract requires.\n */\n removeOtherRecordsForApp: boolean;\n}\n\nexport interface InstallationMarkerResult {\n /**\n * `true` when this is a new installation and the records left behind by a previous one were deleted.\n * Always `false` on Android, where uninstalling the app already deletes its records and keys.\n */\n wiped: boolean;\n}\n\nexport type BiometricsErrorCode =\n | 'user_canceled'\n | 'system_canceled'\n | 'user_fallback'\n | 'authentication_failed'\n | 'biometry_not_available'\n | 'biometry_not_enrolled'\n | 'biometry_lockout'\n | 'passcode_not_set'\n | 'secure_key_storage_unavailable'\n | 'key_not_found'\n | 'key_invalidated'\n | 'key_generation_failed'\n | 'signing_failed'\n | 'storage_failed'\n | 'invalid_argument'\n | 'not_implemented'\n | 'native_module_unavailable'\n | 'unknown';\n"],"mappings":";;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
@@ -0,0 +1,9 @@
1
+ {
2
+ "platforms": ["apple", "android"],
3
+ "apple": {
4
+ "modules": ["ClerkExpoBiometricsModule"]
5
+ },
6
+ "android": {
7
+ "modules": ["expo.modules.clerk.biometrics.ClerkExpoBiometricsModule"]
8
+ }
9
+ }