You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First stable release of the Nitro-based v6 line. Promotes `6.0.0-rc.12` to GA with no API changes — the release notes below summarize everything new since the v5 line.
***rotation:** Add versioned key rotation via `rotateKeys()` and `getKeyVersion()` with lazy re-encryption on read. New `useKeyRotation` hook exposes the same flow declaratively.
8
-
***security hardening:** Defense-in-depth pass — non-breaking, applied transparently to new writes and via lazy upgrade on rotation:
9
-
- HMAC-SHA256 integrity tag bound to every entry's metadata + ciphertext, surfaced on `StorageMetadata.integrityTag`. Tampering with SharedPreferences/Keychain attributes now raises `IntegrityViolationError` (`E_INTEGRITY_VIOLATION`) before any biometric prompt is shown.
10
-
- AES-GCM AAD on Android binds ciphertext to `service|key|v<version>`, defeating cross-entry swap attacks.
11
-
-`setUnlockedDeviceRequired(true)` on every Android Keystore key (API 28+), mirroring iOS's `kSecAttrAccessibleWhenUnlocked` semantics.
12
-
- Plaintext byte buffers are zeroized after use on both platforms.
- Backwards compatible: entries written by earlier versions decode without verification and are upgraded on the next write or rotation.
15
-
***errors:** New typed error classes (`SensitiveInfoError`, `NotFoundError`, `AuthenticationCanceledError`, `IntegrityViolationError`, `KeyInvalidatedError`, `RotationFailedError`) with `code` discriminants and `instanceof` predicates. Importable from the `react-native-sensitive-info/errors` subpath.
16
-
***tree-shaking:**`"sideEffects": false` everywhere; the package now publishes three focused subpath entries (`.`, `/hooks`, `/errors`). The default export has been removed — import only the helpers you use.
17
-
***nitro 0.35:** Regenerated against `nitrogen@0.35.5` and `react-native-nitro-modules@0.35.5`.
18
-
***tooling:** Migrated linting/formatting from ESLint + Prettier to **Biome 2**. Single config at `biome.json`, faster CI runs.
19
-
20
-
### Refactor (KISS · DRY · SRP)
21
-
22
-
* Introduced `useAsyncQuery` (read-only hooks) and `useMutation` (mutation hooks) primitives. `useHasSecret`, `useSecretItem`, `useSecureOperation`, `useKeyRotation`, and `useSecureStorage` now compose the same lifecycle/abort/error-handling pipeline — no duplicated state machines.
23
-
*`useSecureStorage` shrunk from ~230 LOC to ~180 LOC and reuses the shared abort + auth-cancel semantics; behaviour is unchanged.
24
-
* Test fixtures consolidated in `src/__tests__/__mocks__/fixtures.ts` (`buildTestItem`, `buildTestMetadata`).
25
-
* Removed redundant re-exports from `src/internal/errors.ts`.
26
-
27
-
### Breaking changes
5
+
* add AccessControlCard and DiagnosticsCard components; remove unused components ([6701d84](https://github.com/mcodex/react-native-sensitive-info/commit/6701d84226b97cf587064c596f871e8395aa7250))
6
+
* implement integrity hardening for sensitive data storage ([60b2cf5](https://github.com/mcodex/react-native-sensitive-info/commit/60b2cf5c8520cb40f917a698f326a239861e5d10))
28
7
29
-
* The default export is gone. Use named imports: `import { setItem } from 'react-native-sensitive-info'`.
30
-
* React hooks are no longer re-exported from the package root — import them from `react-native-sensitive-info/hooks`.
31
-
32
-
### Notes
33
-
34
-
***iOS rotation** updates the Keychain metadata via `SecItemUpdate`, preserving the original access-control attributes while bumping `keyVersion`.
35
-
***Android rotation** mints a fresh per-entry Keystore alias (`SensitiveInfo_<hash>_v<version>`) during lazy or eager re-encryption and deletes the stale alias after a successful rewrite.
36
-
* Version state lives in a non-secret registry (`SharedPreferences` on Android, `UserDefaults` on iOS). Delete the app's data to reset.
8
+
### Bug Fixes
37
9
10
+
* update module resolver alias to correctly map source directory for subpath imports ([106621e](https://github.com/mcodex/react-native-sensitive-info/commit/106621e8d4c0cba81987196054a971628b4a36d8))
-**Timestamp** — UNIX seconds when the entry was last written.
432
433
433
-
Use `getSupportedSecurityLevels()` to tailor UX before prompting users. For example, disable Secure Enclave options on simulators.
434
+
Use `getSupportedSecurityLevels()` to tailor UX before prompting users. For example, disable Secure Enclave options on simulators. For richer enrollment-state detection (so you can distinguish *"hardware missing"* from *"user hasn't enrolled yet"*), see [👁️ Biometrics](#️-biometrics).
434
435
435
436
> [!TIP]
436
437
> Need to demo biometrics on a simulator? Use Xcode’s “Features → Face ID” and Android Studio’s “Fingerprints” toggles to simulate successful scans.
437
438
439
+
## 👁️ Biometrics
440
+
441
+
The library disambiguates **capability** from **enrollment** so you can render the right UX without false positives. `SecurityAvailability` exposes both a quick boolean (`biometry`) and a fine-grained `biometryStatus` enum:
442
+
443
+
|`biometryStatus`| Meaning | Recommended UX |
444
+
| --- | --- | --- |
445
+
|`'available'`| Hardware present, enrolled, currently usable. | Enable the biometric toggle. |
446
+
|`'notEnrolled'`| Hardware present but no fingerprint/face is registered. | Show a *“Set up Face ID / fingerprint”* CTA that deep-links to settings. |
447
+
|`'notAvailable'`| Missing or permanently disabled (no hardware, admin policy, passcode unset). | Hide the biometric toggle entirely. |
448
+
|`'lockedOut'`| Too many failed attempts; transiently locked. iOS only at probe time — Android surfaces lockout via `BiometricPrompt` failures. | Show *“Try again later”* and offer a `devicePasscode` fallback. |
449
+
|`'unknown'`| Probe could not classify the device. | Treat as `notAvailable` for gating; log for diagnostics. |
450
+
451
+
> Invariant: `biometry === (biometryStatus === 'available')`. Both fields come from the same native probe.
452
+
453
+
### Gate a toggle on a specific access-control policy
454
+
455
+
`canUseAccessControl(policy)` predicts whether a future `setItem` write with the requested policy will succeed on the current device. It maps the policy onto a [`SecurityAvailability`](#-access-control--metadata) snapshot — pure TS, no native call — but if you don't pass a snapshot it first fetches one via `getSupportedSecurityLevels()`. Pass the snapshot you already hold (e.g. from `useSecurityAvailability`) to skip that round-trip:
### Auto-refresh when the user returns from system settings
479
+
480
+
Users commonly leave the app to enroll a fingerprint and come back. Opt into foreground auto-refresh so the toggle reflects the new state without a manual `refetch()`:
analytics.track('biometry_status_changed', { from: previous, to: next })
501
+
if (previous==='notEnrolled'&&next==='available') showToast('Face ID is ready.')
502
+
})
503
+
```
504
+
505
+
It lives in its own module, so apps that don’t need transition tracking don’t pay for it (`sideEffects: false` + named exports keep tree-shaking honest).
506
+
438
507
## 🧪 Simulators and emulators
439
508
440
509
- iOS simulators do not offer Secure Enclave hardware. Biometric prompts usually fall back to a passcode dialog.
0 commit comments