react-native-nitro-storage 0.4.5 → 0.5.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/src/index.ts CHANGED
@@ -21,6 +21,8 @@ import type {
21
21
  import {
22
22
  getStorageErrorCode,
23
23
  isLockedStorageErrorCode,
24
+ type SecureStorageMetadata,
25
+ type SecurityCapabilities,
24
26
  type StorageCapabilities,
25
27
  type StorageErrorCode,
26
28
  } from "./storage-runtime";
@@ -30,6 +32,8 @@ export type { Storage } from "./Storage.nitro";
30
32
  export { migrateFromMMKV } from "./migration";
31
33
  export {
32
34
  getStorageErrorCode,
35
+ type SecureStorageMetadata,
36
+ type SecurityCapabilities,
33
37
  type StorageCapabilities,
34
38
  type StorageErrorCode,
35
39
  } from "./storage-runtime";
@@ -162,6 +166,7 @@ const metricsCounters = new Map<
162
166
  string,
163
167
  { count: number; totalDurationMs: number; maxDurationMs: number }
164
168
  >();
169
+ const nativeSecureBackend = "platform-secure-storage";
165
170
 
166
171
  function recordMetric(
167
172
  operation: string,
@@ -803,7 +808,7 @@ export const storage = {
803
808
  platform: "native",
804
809
  backend: {
805
810
  disk: "platform-preferences",
806
- secure: "platform-secure-storage",
811
+ secure: nativeSecureBackend,
807
812
  },
808
813
  writeBuffering: {
809
814
  disk: true,
@@ -811,6 +816,67 @@ export const storage = {
811
816
  },
812
817
  errorClassification: true,
813
818
  }),
819
+ getSecurityCapabilities: (): SecurityCapabilities => ({
820
+ platform: "native",
821
+ secureStorage: {
822
+ backend: nativeSecureBackend,
823
+ encrypted: "available",
824
+ accessControl: "unknown",
825
+ keychainAccessGroup: "unknown",
826
+ hardwareBacked: "unknown",
827
+ },
828
+ biometric: {
829
+ storage: "unknown",
830
+ prompt: "unknown",
831
+ biometryOnly: "unknown",
832
+ biometryOrPasscode: "unknown",
833
+ },
834
+ metadata: {
835
+ perKey: true,
836
+ listsWithoutValues: true,
837
+ persistsTimestamps: false,
838
+ },
839
+ }),
840
+ getSecureMetadata: (key: string): SecureStorageMetadata => {
841
+ return measureOperation(
842
+ "storage:getSecureMetadata",
843
+ StorageScope.Secure,
844
+ () => {
845
+ flushSecureWrites();
846
+ const storageModule = getStorageModule();
847
+ const biometricProtected = storageModule.hasSecureBiometric(key);
848
+ const exists =
849
+ biometricProtected || storageModule.has(key, StorageScope.Secure);
850
+ let kind: SecureStorageMetadata["kind"] = "missing";
851
+ if (exists) {
852
+ kind = biometricProtected ? "biometric" : "secure";
853
+ }
854
+
855
+ return {
856
+ key,
857
+ exists,
858
+ kind,
859
+ backend: nativeSecureBackend,
860
+ encrypted: "available",
861
+ hardwareBacked: "unknown",
862
+ biometricProtected,
863
+ valueExposed: false,
864
+ };
865
+ },
866
+ );
867
+ },
868
+ getAllSecureMetadata: (): SecureStorageMetadata[] => {
869
+ return measureOperation(
870
+ "storage:getAllSecureMetadata",
871
+ StorageScope.Secure,
872
+ () => {
873
+ flushSecureWrites();
874
+ return getStorageModule()
875
+ .getAllKeys(StorageScope.Secure)
876
+ .map((key) => storage.getSecureMetadata(key));
877
+ },
878
+ );
879
+ },
814
880
  getString: (key: string, scope: StorageScope): string | undefined => {
815
881
  return measureOperation("storage:getString", scope, () => {
816
882
  return getRawValue(key, scope);
package/src/index.web.ts CHANGED
@@ -21,6 +21,8 @@ import {
21
21
  import {
22
22
  getStorageErrorCode,
23
23
  isLockedStorageErrorCode,
24
+ type SecureStorageMetadata,
25
+ type SecurityCapabilities,
24
26
  type StorageCapabilities,
25
27
  type StorageErrorCode,
26
28
  } from "./storage-runtime";
@@ -29,6 +31,8 @@ export { StorageScope, AccessControl, BiometricLevel } from "./Storage.types";
29
31
  export { migrateFromMMKV } from "./migration";
30
32
  export {
31
33
  getStorageErrorCode,
34
+ type SecureStorageMetadata,
35
+ type SecurityCapabilities,
32
36
  type StorageCapabilities,
33
37
  type StorageErrorCode,
34
38
  } from "./storage-runtime";
@@ -261,6 +265,12 @@ function getBackendName(
261
265
  return backend?.name ?? `web:${scopeName}`;
262
266
  }
263
267
 
268
+ function getWebSecureEncryptionStatus(
269
+ backend: WebSecureStorageBackend | undefined,
270
+ ): "unavailable" | "unknown" {
271
+ return backend?.name === "localStorage:secure" ? "unavailable" : "unknown";
272
+ }
273
+
264
274
  function createWebStorageError(
265
275
  scope: NonMemoryScope,
266
276
  operation: string,
@@ -1307,6 +1317,72 @@ export const storage = {
1307
1317
  },
1308
1318
  errorClassification: true,
1309
1319
  }),
1320
+ getSecurityCapabilities: (): SecurityCapabilities => {
1321
+ const secureBackend = getBackendName(
1322
+ StorageScope.Secure,
1323
+ webSecureStorageBackend,
1324
+ );
1325
+ return {
1326
+ platform: "web",
1327
+ secureStorage: {
1328
+ backend: secureBackend,
1329
+ encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
1330
+ accessControl: "unavailable",
1331
+ keychainAccessGroup: "unavailable",
1332
+ hardwareBacked: "unavailable",
1333
+ },
1334
+ biometric: {
1335
+ storage: "unavailable",
1336
+ prompt: "unavailable",
1337
+ biometryOnly: "unavailable",
1338
+ biometryOrPasscode: "unavailable",
1339
+ },
1340
+ metadata: {
1341
+ perKey: true,
1342
+ listsWithoutValues: true,
1343
+ persistsTimestamps: false,
1344
+ },
1345
+ };
1346
+ },
1347
+ getSecureMetadata: (key: string): SecureStorageMetadata => {
1348
+ return measureOperation(
1349
+ "storage:getSecureMetadata",
1350
+ StorageScope.Secure,
1351
+ () => {
1352
+ flushSecureWrites();
1353
+ const biometricProtected = WebStorage.hasSecureBiometric(key);
1354
+ const exists =
1355
+ biometricProtected || WebStorage.has(key, StorageScope.Secure);
1356
+ let kind: SecureStorageMetadata["kind"] = "missing";
1357
+ if (exists) {
1358
+ kind = biometricProtected ? "biometric" : "secure";
1359
+ }
1360
+
1361
+ return {
1362
+ key,
1363
+ exists,
1364
+ kind,
1365
+ backend: getBackendName(StorageScope.Secure, webSecureStorageBackend),
1366
+ encrypted: getWebSecureEncryptionStatus(webSecureStorageBackend),
1367
+ hardwareBacked: "unavailable",
1368
+ biometricProtected,
1369
+ valueExposed: false,
1370
+ };
1371
+ },
1372
+ );
1373
+ },
1374
+ getAllSecureMetadata: (): SecureStorageMetadata[] => {
1375
+ return measureOperation(
1376
+ "storage:getAllSecureMetadata",
1377
+ StorageScope.Secure,
1378
+ () => {
1379
+ flushSecureWrites();
1380
+ return WebStorage.getAllKeys(StorageScope.Secure).map((key) =>
1381
+ storage.getSecureMetadata(key),
1382
+ );
1383
+ },
1384
+ );
1385
+ },
1310
1386
  getString: (key: string, scope: StorageScope): string | undefined => {
1311
1387
  return measureOperation("storage:getString", scope, () => {
1312
1388
  return getRawValue(key, scope);
@@ -19,6 +19,41 @@ export type StorageCapabilities = {
19
19
  errorClassification: boolean;
20
20
  };
21
21
 
22
+ export type SecurityCapabilityStatus = "available" | "unavailable" | "unknown";
23
+
24
+ export type SecurityCapabilities = {
25
+ platform: "native" | "web";
26
+ secureStorage: {
27
+ backend: string;
28
+ encrypted: SecurityCapabilityStatus;
29
+ accessControl: SecurityCapabilityStatus;
30
+ keychainAccessGroup: SecurityCapabilityStatus;
31
+ hardwareBacked: SecurityCapabilityStatus;
32
+ };
33
+ biometric: {
34
+ storage: SecurityCapabilityStatus;
35
+ prompt: SecurityCapabilityStatus;
36
+ biometryOnly: SecurityCapabilityStatus;
37
+ biometryOrPasscode: SecurityCapabilityStatus;
38
+ };
39
+ metadata: {
40
+ perKey: boolean;
41
+ listsWithoutValues: boolean;
42
+ persistsTimestamps: boolean;
43
+ };
44
+ };
45
+
46
+ export type SecureStorageMetadata = {
47
+ key: string;
48
+ exists: boolean;
49
+ kind: "secure" | "biometric" | "missing";
50
+ backend: string;
51
+ encrypted: SecurityCapabilityStatus;
52
+ hardwareBacked: SecurityCapabilityStatus;
53
+ biometricProtected: boolean;
54
+ valueExposed: false;
55
+ };
56
+
22
57
  const STORAGE_ERROR_TAG_PATTERN = /\[nitro-error:([a-z_]+)\]/;
23
58
 
24
59
  export function getStorageErrorCode(