Skip to content

Commit 7f942a0

Browse files
committed
feat: Add SecurityCapabilitiesDemo component and integrate security capabilities checks
- Updated package.json to include new dependencies for navigation and security context. - Introduced SecurityCapabilitiesDemo component to display device security capabilities and test smart fallback functionality. - Enhanced App component to include SecurityCapabilitiesDemo. - Implemented getSecurityCapabilities function to retrieve device security features. - Modified iOS SensitiveInfo.swift to support smart fallback for security levels. - Updated index.ts to export new security capabilities function.
1 parent d7a9537 commit 7f942a0

10 files changed

Lines changed: 1110 additions & 42 deletions

File tree

README.md

Lines changed: 203 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,13 @@ Experience next-generation performance with direct JSI bindings, zero bridge ove
2222

2323
- **⚡ Lightning Fast**: Nitro modules with direct JSI bindings (no bridge overhead)
2424
- **🔒 Hardware Security**: iOS Secure Enclave + Android StrongBox (when available)
25-
- **🛡️ Multi-Level Security**: Automatic fallback from hardware → biometric → standard encryption
26-
- **📱 Cross-Platform**: Unified API for iOS, Android, and macOS (Windows deprecated in v6.0.0)
27-
- **🎯 Modern API**: Clean TypeScript interface with Promise-based methods
25+
- **🛡️ Smart Fallback**: Automatic graceful degradation from hardware → biometric → standard encryption
26+
- **📱 Universal Compatibility**: Works seamlessly on ALL devices and emulators (no setup required)
27+
- **🎯 Modern API**: Clean TypeScript interface with Promise-based methods and comprehensive capabilities detection
2828
- **🎨 TypeScript First**: Full type safety with auto-generated definitions
2929
- **🌟 SwiftUI Ready**: HybridView component for native SwiftUI integration
3030
- **🍎 macOS Support**: Touch ID integration for desktop applications
31+
- **🧪 Developer Friendly**: Zero emulator issues, informative fallback warnings, comprehensive debugging tools
3132

3233
---
3334

@@ -98,6 +99,26 @@ await clear();
9899
| `clear(options?)` | Remove all data | `Promise<void>` |
99100
| `isBiometricAvailable()` | Check biometric availability | `Promise<boolean>` |
100101
| `isStrongBoxAvailable()` | Check hardware security availability | `Promise<boolean>` |
102+
| `getSecurityCapabilities()` | Get comprehensive device security info | `Promise<SecurityCapabilities>` |
103+
104+
### Security Capabilities
105+
106+
```typescript
107+
interface SecurityCapabilities {
108+
biometric: boolean;
109+
strongbox: boolean;
110+
recommendedLevel: 'standard' | 'biometric' | 'strongbox';
111+
}
112+
113+
// Check what your device supports
114+
const capabilities = await getSecurityCapabilities();
115+
console.log(capabilities);
116+
// {
117+
// biometric: false, // Touch ID/Face ID/Fingerprint unavailable
118+
// strongbox: false, // Hardware security module unavailable
119+
// recommendedLevel: 'standard' // Best available security level
120+
// }
121+
```
101122

102123
### Security Levels
103124

@@ -129,45 +150,80 @@ interface BiometricOptions {
129150
### Examples
130151

131152
```typescript
132-
// Basic usage - uses optimal security automatically
153+
// 🎯 Smart device-aware storage
154+
const capabilities = await getSecurityCapabilities();
155+
console.log(capabilities);
156+
// { biometric: true, strongbox: false, recommendedLevel: 'biometric' }
157+
158+
// Store using recommended security level
159+
await setItem('sensitiveData', 'value', {
160+
securityLevel: capabilities.recommendedLevel
161+
});
162+
163+
// 🔧 Basic usage - automatic optimal security
133164
await setItem('userToken', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...');
134165
const token = await getItem('userToken');
135166

136-
// Explicit security levels
167+
// 🛡️ Explicit security levels with smart fallback
137168
await setItem('publicData', 'value', { securityLevel: 'standard' });
169+
138170
await setItem('biometricData', 'sensitive', {
139-
securityLevel: 'biometric',
171+
securityLevel: 'biometric', // Falls back to strongbox/standard if unavailable
140172
biometricOptions: {
141173
promptTitle: 'Access Required',
142174
promptDescription: 'Please authenticate to access your data',
143175
allowDeviceCredential: true
144176
}
145177
});
178+
146179
await setItem('highSecurityData', 'top-secret', {
147-
securityLevel: 'strongbox'
180+
securityLevel: 'strongbox' // Falls back to biometric/standard if unavailable
148181
});
149182

150-
// Check device capabilities
151-
const canUseBiometric = await isBiometricAvailable();
152-
const hasHardwareSecurity = await isStrongBoxAvailable();
183+
// 📱 Emulator-friendly approach (works everywhere!)
184+
await setItem('devData', 'test-value', {
185+
securityLevel: 'biometric' // No errors on emulators - auto fallback
186+
});
187+
188+
// 🎛️ Device capability checking
189+
const { biometric, strongbox, recommendedLevel } = await getSecurityCapabilities();
153190

154-
if (hasHardwareSecurity) {
155-
await setItem('critical-data', 'value', { securityLevel: 'strongbox' });
156-
} else if (canUseBiometric) {
157-
await setItem('critical-data', 'value', { securityLevel: 'biometric' });
191+
if (strongbox) {
192+
console.log('🛡️ Hardware security available - maximum protection');
193+
} else if (biometric) {
194+
console.log('👆 Biometric security available - enhanced protection');
158195
} else {
159-
await setItem('critical-data', 'value', { securityLevel: 'standard' });
196+
console.log('🔐 Standard security available - basic protection');
160197
}
161198

162-
// Complex data storage
163-
await setItem('userProfile', JSON.stringify({
199+
// 🗂️ Complex data storage with automatic security
200+
const userProfile = {
164201
id: 123,
165202
email: 'user@example.com',
166-
preferences: { theme: 'dark' }
167-
}), { securityLevel: 'biometric' });
203+
preferences: { theme: 'dark' },
204+
securitySettings: { twoFactorEnabled: true }
205+
};
206+
207+
await setItem('userProfile', JSON.stringify(userProfile), {
208+
securityLevel: recommendedLevel // Uses best available automatically
209+
});
168210

169211
const profileStr = await getItem('userProfile');
170212
const profile = profileStr ? JSON.parse(profileStr) : null;
213+
214+
// 🧪 Test different security levels (development)
215+
const testData = 'test-value-' + Date.now();
216+
217+
// Test all security levels - library handles fallbacks automatically
218+
for (const level of ['standard', 'biometric', 'strongbox'] as const) {
219+
try {
220+
await setItem(`test-${level}`, testData, { securityLevel: level });
221+
const retrieved = await getItem(`test-${level}`, { securityLevel: level });
222+
console.log(`✅ ${level}: ${retrieved === testData ? 'SUCCESS' : 'FAILED'}`);
223+
} catch (error) {
224+
console.log(`❌ ${level}: FAILED -`, error.message);
225+
}
226+
}
171227
```
172228

173229
---
@@ -209,6 +265,44 @@ await setItem('sensitiveData', 'value', {
209265
// Check device capabilities
210266
const hasStrongBox = await isStrongBoxAvailable();
211267
const hasBiometric = await isBiometricAvailable();
268+
269+
// Get comprehensive security info
270+
const capabilities = await getSecurityCapabilities();
271+
console.log(capabilities);
272+
// {
273+
// biometric: false,
274+
// strongbox: false,
275+
// recommendedLevel: 'standard'
276+
// }
277+
```
278+
279+
### 🔄 Smart Fallback System
280+
281+
The library implements intelligent fallback behavior when requested security features aren't available:
282+
283+
**Biometric Security Request:**
284+
1. ✅ Biometric available → Uses biometric protection
285+
2. ❌ Biometric unavailable but StrongBox available → Falls back to StrongBox
286+
3. ❌ Neither available → Falls back to standard encryption
287+
288+
**StrongBox Security Request:**
289+
1. ✅ StrongBox available → Uses hardware security
290+
2. ❌ StrongBox unavailable but biometric available → Falls back to biometric
291+
3. ❌ Neither available → Falls back to standard encryption
292+
293+
**Emulator/Simulator Support:**
294+
- ✅ Works seamlessly on all emulators
295+
- ⚠️ Automatically falls back to standard encryption
296+
- 📝 Console warnings inform about fallback behavior
297+
- 🔧 Perfect for development and testing
298+
299+
```typescript
300+
// This works on ALL devices and emulators!
301+
await setItem('secret', 'value', {
302+
securityLevel: 'biometric'
303+
});
304+
// On emulator: Falls back to standard encryption
305+
// On device: Uses biometric if available, otherwise fallback
212306
```
213307

214308
### Security Features by Platform
@@ -258,6 +352,53 @@ const hasBiometric = await isBiometricAvailable();
258352

259353
---
260354

355+
## ✨ What's New in v6.0.0+
356+
357+
### 🔄 Smart Fallback System
358+
No more errors on emulators or devices without biometric hardware! The library now automatically falls back to available security levels:
359+
360+
```typescript
361+
// ✅ Works on ALL devices - no more "biometric not available" errors!
362+
await setItem('data', 'value', { securityLevel: 'biometric' });
363+
// Emulator: Falls back to standard encryption
364+
// Device: Uses biometric if available, otherwise fallback
365+
```
366+
367+
### 📊 Security Capabilities API
368+
New comprehensive device analysis:
369+
370+
```typescript
371+
const capabilities = await getSecurityCapabilities();
372+
// {
373+
// biometric: false, // Touch ID/Face ID availability
374+
// strongbox: false, // Hardware security module availability
375+
// recommendedLevel: 'standard' // Best security level for this device
376+
// }
377+
```
378+
379+
### 🎯 Intelligent Security Selection
380+
The library now provides intelligent recommendations:
381+
382+
```typescript
383+
// Use the best available security automatically
384+
const { recommendedLevel } = await getSecurityCapabilities();
385+
await setItem('sensitiveData', 'value', { securityLevel: recommendedLevel });
386+
```
387+
388+
### 🔧 Enhanced Developer Experience
389+
-**Zero emulator issues** - Smart fallback handles everything
390+
- 📝 **Informative console warnings** when fallbacks occur
391+
- 🧪 **Comprehensive debugging tools** with `getSecurityCapabilities()`
392+
- 🎨 **Better TypeScript support** with detailed capability types
393+
394+
### 🛡️ Production-Ready Security
395+
- 🔒 **Hardware security** when available (Secure Enclave/StrongBox)
396+
- 👆 **Biometric protection** with seamless fallback
397+
- 🔐 **Standard encryption** as universal baseline
398+
- 🎯 **Automatic optimization** for each device's capabilities
399+
400+
---
401+
261402
## 🔄 Migration from v5.x.x to v6.0.0
262403

263404
### Breaking Changes
@@ -572,14 +713,32 @@ struct CustomSecureView: View {
572713

573714
### Common Issues
574715

716+
#### ✅ Emulator & Simulator Support
717+
- **"Biometric authentication is not available"**: ✅ **RESOLVED** - v6.0.0+ automatically falls back to available security
718+
- **Emulator compatibility**: ✅ **WORKS** - All features work on emulators with automatic fallback
719+
- **Simulator limitations**: ✅ **HANDLED** - iOS Simulator automatically uses standard keychain with console warnings
720+
- **Development workflow**: ✅ **SEAMLESS** - No special configuration needed for development/testing
721+
722+
#### Smart Fallback System
723+
```typescript
724+
// ✅ This works on ALL devices and emulators without errors!
725+
await setItem('data', 'value', { securityLevel: 'biometric' });
726+
727+
// Check what actually happened:
728+
const caps = await getSecurityCapabilities();
729+
console.log(`Requested: biometric, Using: ${caps.recommendedLevel}`);
730+
// Emulator output: "Requested: biometric, Using: standard"
731+
// iPhone output: "Requested: biometric, Using: biometric"
732+
```
733+
575734
#### iOS Issues
576-
- **"Secure Enclave not available"**: Normal on older devices or simulators, library automatically falls back to standard keychain
735+
- **"Secure Enclave not available"**: ✅ Auto-fallback enabled - check console for fallback notifications
577736
- **"Module 'NitroModules' not found"**: Run `cd ios && pod install` and ensure React Native 0.70+
578-
- **Simulator limitations**: Some Keychain features are limited on iOS Simulator, test on real devices
737+
- **Simulator limitations**: ✅ Handled automatically with smart fallback system
579738
- **CocoaPods cache issues**: `cd ios && rm -rf Pods Podfile.lock && pod install && pod update`
580739

581740
#### Android Issues
582-
- **"StrongBox not available"**: Expected on older devices, library falls back to standard Android Keystore
741+
- **"StrongBox not available"**: ✅ Auto-fallback enabled - library gracefully degrades security level
583742
- **Build errors with Nitro**: Clean and rebuild with `cd android && ./gradlew clean && cd .. && npx react-native run-android`
584743
- **ProGuard/R8 issues**: Add keep rules for Nitro modules in `proguard-rules.pro`
585744

@@ -590,16 +749,32 @@ struct CustomSecureView: View {
590749

591750
### Debug Mode
592751

593-
Enable debug logging to troubleshoot issues:
752+
Enable comprehensive debugging to troubleshoot issues:
594753

595754
```typescript
596-
// Enable debug mode (development only)
755+
// 🔍 Comprehensive security analysis (development only)
597756
if (__DEV__) {
598-
// Add debug logging in your app
599-
console.log('Security capabilities:', {
600-
biometric: await isBiometricAvailable(),
601-
strongbox: await isStrongBoxAvailable()
757+
const capabilities = await getSecurityCapabilities();
758+
console.log('📊 Security Analysis:', {
759+
biometric: capabilities.biometric,
760+
strongbox: capabilities.strongbox,
761+
recommendedLevel: capabilities.recommendedLevel,
762+
platform: Platform.OS,
763+
isEmulator: await DeviceInfo.isEmulator?.() || 'unknown'
602764
});
765+
766+
// Test all security levels
767+
const testResults = {};
768+
for (const level of ['standard', 'biometric', 'strongbox'] as const) {
769+
try {
770+
await setItem(`debug-test-${level}`, 'test', { securityLevel: level });
771+
await removeItem(`debug-test-${level}`, { securityLevel: level });
772+
testResults[level] = '✅ Working';
773+
} catch (error) {
774+
testResults[level] = `❌ ${error.message}`;
775+
}
776+
}
777+
console.log('🧪 Security Level Tests:', testResults);
603778
}
604779
```
605780

android/src/main/java/com/margelo/nitro/sensitiveinfo/SensitiveInfo.kt

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,28 @@ class SensitiveInfo : HybridSensitiveInfoSpec() {
8989

9090
private fun getPreferencesForSecurityLevel(securityLevel: SecurityLevel?): android.content.SharedPreferences {
9191
return when (securityLevel) {
92-
SecurityLevel.BIOMETRIC -> biometricPrefs
93-
SecurityLevel.STRONGBOX -> strongBoxPrefs
92+
SecurityLevel.BIOMETRIC -> {
93+
// Check if biometric authentication is available
94+
if (isBiometricAvailableInternal()) {
95+
biometricPrefs
96+
} else {
97+
// Fallback to StrongBox if available, otherwise standard
98+
val fallbackLevel = if (isStrongBoxAvailableInternal()) "strongbox" else "standard"
99+
println("⚠️ SensitiveInfo: Biometric authentication not available, falling back to $fallbackLevel")
100+
if (isStrongBoxAvailableInternal()) strongBoxPrefs else standardPrefs
101+
}
102+
}
103+
SecurityLevel.STRONGBOX -> {
104+
// Check if StrongBox is available
105+
if (isStrongBoxAvailableInternal()) {
106+
strongBoxPrefs
107+
} else {
108+
// Fallback to biometric if available, otherwise standard
109+
val fallbackLevel = if (isBiometricAvailableInternal()) "biometric" else "standard"
110+
println("⚠️ SensitiveInfo: StrongBox not available, falling back to $fallbackLevel")
111+
if (isBiometricAvailableInternal()) biometricPrefs else standardPrefs
112+
}
113+
}
94114
else -> standardPrefs
95115
}
96116
}
@@ -132,18 +152,22 @@ class SensitiveInfo : HybridSensitiveInfoSpec() {
132152

133153
@DoNotStrip
134154
override fun isBiometricAvailable(): Promise<Boolean> = Promise.async {
135-
val biometricManager = BiometricManager.from(context)
136-
when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
137-
BiometricManager.BIOMETRIC_SUCCESS -> true
138-
else -> false
139-
}
155+
isBiometricAvailableInternal()
140156
}
141157

142158
@DoNotStrip
143159
override fun isStrongBoxAvailable(): Promise<Boolean> = Promise.async {
144160
isStrongBoxAvailableInternal()
145161
}
146162

163+
private fun isBiometricAvailableInternal(): Boolean {
164+
val biometricManager = BiometricManager.from(context)
165+
return when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_STRONG)) {
166+
BiometricManager.BIOMETRIC_SUCCESS -> true
167+
else -> false
168+
}
169+
}
170+
147171
private fun isStrongBoxAvailableInternal(): Boolean {
148172
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
149173
try {

0 commit comments

Comments
 (0)