Skip to content

Commit ec935c8

Browse files
committed
Refactor iOS Keychain & biometric flows; add access-control resolver, metadata, and docs
1 parent 19a4d56 commit ec935c8

File tree

7 files changed

+365
-263
lines changed

7 files changed

+365
-263
lines changed

README.md

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,41 @@
66
[![downloads](https://img.shields.io/npm/dm/react-native-sensitive-info.svg?style=flat-square)](https://www.npmjs.com/package/react-native-sensitive-info)
77
[![license](https://img.shields.io/npm/l/react-native-sensitive-info.svg?style=flat-square)](LICENSE)
88

9-
**v5.6.0** - Production Ready | iOS 13+ | Android 8+ | macOS 10.15+ | visionOS 1.0+ | watchOS 6+
9+
**v5.6.0** - Production Ready | iOS 13+ | Android 7+ | macOS 10.15+ | visionOS 1.0+ | watchOS 6+
1010

1111
---
1212

1313
## ⚠️ Version Information
1414

15-
> **v5.6.0 (Current - Legacy Support)**
15+
> [!TIP]
16+
> **v6.0.0 (Nitro Preview)**
1617
>
17-
> -**Works with React Native New Architecture**
18-
> - ✅ Production ready with complete Android/iOS implementation
19-
> - ✅ Biometric authentication (Face ID, Touch ID, Fingerprint)
20-
> - ✅ AES-256-GCM encryption with hardware-backed keys
21-
> - ⚠️ **Legacy support branch** - No new features planned
22-
> - 📦 Uses TurboModules (React Native bridge)
23-
24-
> **v5.5.x and Older**
25-
>
26-
> - ✅ Works with **React Native Old Architecture only**
27-
> - ✅ Legacy support, bug fixes only
28-
> - 📝 Use this version if you cannot use New Architecture
18+
> - 🚀 Adds full Nitro Module and Nitro View compatibility
19+
> - 🧪 Currently stabilizing on the `master` branch
20+
> - ⏳ Preview quality while documentation and samples are updated
2921
30-
> **v6.x (Master Branch - Next Generation)**
22+
> [!IMPORTANT]
23+
> **v5.6.0 (Current)**
24+
>
25+
> - ✅ New Architecture ready (RN 0.73+)
26+
> - ✅ Universal Android authentication (API 24+)
27+
> - ✅ Biometric + device credential support
28+
> - ✅ AES-256-GCM hardware-backed encryption
29+
> - 📦 Distributed as a TurboModule
30+
31+
> [!NOTE]
32+
> **v5.5.x and older**
3133
>
32-
> - 🚀 **Upcoming release** with Nitro Modules
33-
> - 📍 On `master` branch
34-
> - 🔄 Better performance with Nitro native modules
35-
> -**Not yet released** - Check back soon
36-
> - 🔗 Branch: `git checkout master`
34+
> - ✅ Old Architecture only
35+
> - 🛠 Bug fixes only, no new features
36+
> - ⬇️ Install when you cannot move to RN New Architecture yet
3737
38-
**Choose your version based on your React Native architecture:**
38+
**Install the right build for your project:**
3939
```bash
40-
# New Architecture (RN 0.73+, recommended)
40+
# React Native 0.73+ (New Architecture)
4141
npm install [email protected]
4242

43-
# Old Architecture (RN <0.73)
43+
# React Native <0.73 (Old Architecture)
4444
npm install [email protected]
4545
```
4646

@@ -52,8 +52,8 @@ npm install [email protected]
5252
|---------|-----|---------|-------|----------|---------|
5353
| **Secure Storage** ||||||
5454
| **AES-256 Encryption** ||||||
55-
| **Hardware-Backed Keys** ||||||
56-
| **Biometric Auth** | ✅ Face/Touch ID | ✅ Fingerprint | ✅ Touch ID | ✅ Optic ID | ❌ Passcode only |
55+
| **Hardware-Backed Keys** ||API 24+ ||||
56+
| **Biometric Auth** | ✅ Face/Touch ID | ✅ Fingerprint & device credential | ✅ Touch ID | ✅ Optic ID | ❌ Passcode only |
5757
| **Automatic Migration** ||||||
5858
| **Zero Dependencies** ||||||
5959

@@ -70,7 +70,7 @@ AES-256-GCM Encryption (random IV per operation)
7070
7171
Hardware-Backed Key Storage
7272
├─ iOS: Keychain + Secure Enclave (iOS 16+)
73-
├─ Android: AndroidKeyStore + StrongBox (Android 9+)
73+
├─ Android: AndroidKeyStore (StrongBox when available)
7474
└─ Optional: Biometric authentication required
7575
7676
Encrypted data stored securely
@@ -86,6 +86,21 @@ Encrypted data stored securely
8686

8787
---
8888

89+
## 🤖 Android Authentication Matrix
90+
91+
| Android Version | API Level | Prompt Strategy | Storage Backend |
92+
|-----------------|-----------|-----------------|-----------------|
93+
| Android 14 - 10 | 34 - 29 | Keystore-gated biometric/device credential prompt shown when decrypting | AndroidKeyStore (hardware-backed, StrongBox when available) |
94+
| Android 9 - 7 | 28 - 24 | Manual prompt displayed before key use; library waits for user confirmation | AndroidKeyStore (hardware-backed) |
95+
96+
> [!TIP]
97+
> Supply localized `authenticationPrompt` copy even on Android 10+ so users see consistent messaging across API levels.
98+
99+
> [!CAUTION]
100+
> Android 7-9 throws `E_AUTHENTICATION_REQUIRED` if you skip the prompt configuration because the OS does not present a system dialog automatically.
101+
102+
---
103+
89104
## 🚀 Installation
90105

91106
```bash
@@ -109,6 +124,9 @@ Add permissions to `AndroidManifest.xml`:
109124
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
110125
```
111126

127+
> [!NOTE]
128+
> Android 7-9 rely on a manual authentication dialog. Provide `authenticationPrompt` strings whenever you request biometric/device credential protection so the library can display localized UI ahead of decrypting secrets.
129+
112130
### iOS Setup
113131

114132
Add to `Info.plist`:
@@ -151,7 +169,7 @@ const result = await SensitiveInfo.getItem('auth-token', {
151169
});
152170

153171
console.log(result.value); // 'jwt-token-xyz'
154-
console.log(result.metadata); // { securityLevel: 'biometry', ... }
172+
console.log(result.metadata); // { securityLevel: 'strongBox', backend: 'androidKeystore', timestamp: 1700000000 }
155173

156174
// If biometric-protected: OS shows prompt automatically
157175
// User authenticates → value is decrypted and returned
@@ -199,8 +217,8 @@ await SensitiveInfo.clearService({
199217
|----------|---------|-----------------|-------|
200218
| iOS 16+ | Keychain + Secure Enclave | ✅ Yes | Isolated, tamper-resistant |
201219
| iOS 13-15 | Keychain only | ✅ Yes | Device passcode/biometric |
202-
| Android 9+ | AndroidKeyStore + StrongBox | ✅ Yes | Isolated secure processor |
203-
| Android 8 | AndroidKeyStore | ✅ Yes | Software-backed fallback |
220+
| Android 14-10 | AndroidKeyStore + StrongBox | ✅ Yes | System prompt wraps keystore auth |
221+
| Android 9-7 | AndroidKeyStore | ✅ Yes | Manual prompt required before key use |
204222
| macOS 13+ | Keychain + Secure Enclave | ✅ Yes | Touch ID support |
205223
| visionOS | Keychain + Secure Enclave | ✅ Yes | Optic ID support |
206224
| watchOS | Keychain | ✅ Partial | Shared with paired iPhone |
@@ -215,6 +233,9 @@ When enabled, biometric authentication is required to access encrypted data:
215233
- On success: Key is unlocked, value is decrypted
216234
- On failure/cancellation: Access denied, exception thrown
217235

236+
> [!NOTE]
237+
> On Android 7-9 the library displays its own dialog before touching the keystore. On Android 10+ the OS handles the biometric/device credential UI directly.
238+
218239
**Protection against**:
219240
- ✅ Screen reading (encryption)
220241
- ✅ Device theft (biometric + device passcode)
@@ -234,9 +255,9 @@ interface SetOptions {
234255
keychainService?: string; // Service namespace (app package by default)
235256
accessControl?: string; // 'biometryOrDevicePasscode' | 'devicePasscode' | 'none'
236257
authenticationPrompt?: {
237-
title: string; // Required: "Authenticate"
258+
title: string; // Required: "Authenticate" (mandatory on Android 7-9)
238259
subtitle?: string; // "Scan your fingerprint"
239-
description?: string; // "Required to protect this data"
260+
description?: string; // "Required to protect this data" (shown in manual dialog)
240261
};
241262
}
242263

@@ -374,6 +395,9 @@ await testBiometricCancellation();
374395
await testBiometricTimeout();
375396
```
376397

398+
> [!TIP]
399+
> Run your suite on both an Android 13+ emulator **and** an Android 8/9 emulator to validate the automatic keystore dialog and the manual pre-auth dialog paths.
400+
377401
---
378402

379403
## 📖 Platform-Specific Notes
@@ -387,10 +411,10 @@ await testBiometricTimeout();
387411

388412
### Android
389413

390-
- **StrongBox**: Available Android 9+ (secure processor)
391-
- **AndroidKeyStore**: Isolated key storage
392-
- **Hardware Requirements**: Biometric sensor for fingerprint
393-
- **Permissions**: USE_BIOMETRIC + USE_FINGERPRINT in AndroidManifest.xml
414+
- **Authentication Flow**: API 29+ (Android 10+) relies on keystore-gated OS dialogs, while API 24-28 (Android 7-9) shows a library-provided dialog before key use.
415+
- **StrongBox**: Hardware-backed keys automatically prefer StrongBox where present (Android 9+).
416+
- **Activity Hook**: Ensure `ActivityContextHolder.setActivity(this)` runs in `MainActivity.onCreate` so prompts attach to the foreground activity.
417+
- **Permissions**: Declare `USE_BIOMETRIC` and `USE_FINGERPRINT` in `AndroidManifest.xml`.
394418

395419
### macOS
396420

@@ -505,6 +529,11 @@ try {
505529
}
506530
```
507531
532+
### `E_AUTHENTICATION_REQUIRED` on Android 7-9
533+
534+
**Cause**: `authenticationPrompt` text is missing, so the manual dialog cannot be rendered before hitting the keystore.
535+
**Solution**: Provide at least a `title` (and ideally description) when storing or reading biometric-protected secrets.
536+
508537
### "Key has been invalidated"
509538
510539
**Cause**: Biometric enrollment changed (finger added/removed)

example/README.md

Lines changed: 7 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
This is a new [**React Native**](https://reactnative.dev) project, bootstrapped using [`@react-native-community/cli`](https://github.com/react-native-community/cli).
1+
This example showcases the latest `react-native-sensitive-info` flows, including the manual biometric prompt path for Android 7-9 and the keystore-gated dialog on Android 10+.
22

33
# Getting Started
44

5-
> **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding.
5+
> [!NOTE]
6+
> Follow the [main README](../README.md) for library-specific setup details such as permissions and biometric prompt strings.
67
78
## Step 1: Start Metro
89

@@ -32,6 +33,9 @@ npm run android
3233
yarn android
3334
```
3435

36+
> [!TIP]
37+
> Run the app on an Android 13 emulator **and** an Android 8/9 emulator to verify both authentication flows.
38+
3539
### iOS
3640

3741
For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps).
@@ -75,23 +79,4 @@ When you want to forcefully reload, for example to reset the state of your app,
7579

7680
## Congratulations! :tada:
7781

78-
You've successfully run and modified your React Native App. :partying_face:
79-
80-
### Now what?
81-
82-
- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps).
83-
- If you're curious to learn more about React Native, check out the [docs](https://reactnative.dev/docs/getting-started).
84-
85-
# Troubleshooting
86-
87-
If you're having issues getting the above steps to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page.
88-
89-
# Learn More
90-
91-
To learn more about React Native, take a look at the following resources:
92-
93-
- [React Native Website](https://reactnative.dev) - learn more about React Native.
94-
- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment.
95-
- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**.
96-
- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts.
97-
- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native.
82+
You've successfully run and modified the example. Switch between emulators or devices to experience both Android authentication paths and to confirm iOS prompt metadata is surfaced correctly in the console logs.

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2658,7 +2658,7 @@ SPEC CHECKSUMS:
26582658
ReactAppDependencyProvider: a45ef34bb22dc1c9b2ac1f74167d9a28af961176
26592659
ReactCodegen: 878add6c7d8ff8cea87697c44d29c03b79b6f2d9
26602660
ReactCommon: 804dc80944fa90b86800b43c871742ec005ca424
2661-
SensitiveInfo: e8eb8c7587b9109025a62e514129ec29b4550239
2661+
SensitiveInfo: 43398c28c0f14495b190af5d397a889e38799c82
26622662
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
26632663
Yoga: 689c8e04277f3ad631e60fe2a08e41d411daf8eb
26642664

0 commit comments

Comments
 (0)