rn-secure-keystore
v1.0.0
Published
A comprehensive, cross-platform React Native wrapper for secure key-value storage using native security features of Android and iOS. It supports **biometric authentication**, **hardware-backed encryption**, and deep platform integrations such as **Android
Maintainers
Readme
rn-secure-keystore
A comprehensive, cross-platform React Native wrapper for secure key-value storage using native security features of Android and iOS. It supports biometric authentication, hardware-backed encryption, and deep platform integrations such as Android StrongBox, EncryptedSharedPreferences, and iOS Secure Enclave via the Keychain.
This library enables storing data securely with biometric protection, ensuring that sensitive information can only be accessed after successful biometric verification (e.g., fingerprint or face recognition). This adds an additional layer of security by requiring user authentication to retrieve or modify protected data, making it ideal for handling highly sensitive data.
Features
- Hardware-backed security – Utilizes device secure elements when available
- Biometric authentication – Store and retrieve data with biometric protection
- Cross-platform – Works on both iOS and Android with platform-specific optimizations
- StrongBox support (Android only) – Enhanced protection using dedicated security chips
- iOS Keychain integration – Access to iOS Keychain access controls
- Secure storage – Store sensitive data like tokens, passwords, and user credentials
- Error handling – Comprehensive error codes and recovery mechanisms
- Security introspection – Check security levels and platform capabilities
- Fallback mechanisms – Graceful degradation when advanced features aren't available
📦 Installation
npm install rn-secure-keystoreiOS Setup
cd ios && pod installAdd the following to your Info.plist:
<key>NSFaceIDUsageDescription</key>
<string>Use Face ID to authenticate and access secure data</string>Android Setup
Ensure your android/app/build.gradle has minimum SDK version 23:
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 23
// ...
}
}Add biometric permissions to android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />Note: This library requires native modules and will not work with Expo Go.
Platform-Level Security Features
iOS – Secure Enclave & Keychain
On iOS, all data is securely stored using the native Keychain. For devices with Secure Enclave (iPhone 5s and later), encryption is hardware-backed, meaning your secrets are tied to the physical chip and are secure.
Key benefits:
- Secure Enclave for hardware-backed storage
- Support for access groups and keychain sharing between apps
- Advanced access control options
- Hardware backing when available
Android – EncryptedSharedPreferences & StrongBox
EncryptedSharedPreferences
This library uses Android Jetpack's EncryptedSharedPreferences with fallback to regular SharedPreferences, which combines:
- A master key stored in Android's Keystore
- AES encryption for your key-value pairs
- Automatic key generation and rotation
- Graceful fallback for compatibility
StrongBox
On supported devices (API 28+), you can optionally store encryption keys in StrongBox, a hardware security module (HSM) separate from the main CPU. This provides maximum protection, especially against physical tampering.
- Resistant to rooting and debugging
- Keys stored in StrongBox are non-exportable
- Enforced user authentication (e.g., biometrics) via hardware-backed checks
- Fallback to TEE (Trusted Execution Environment) if StrongBox is unavailable
StrongBox is only available on select newer Android devices with a dedicated secure element (Pixel 3+, Samsung S9+, etc.).
🔐 Security Levels
Android Security Tiers
| Level | Description | Use Case |
| ----------- | -------------------------------------------------------- | -------------------- |
| strongbox | Stored in StrongBox HSM; maximum protection if available | Ultra-sensitive data |
| hardware | Backed by Trusted Execution Environment (TEE) | Sensitive data |
| software | Software-encrypted; fallback for older devices | Basic protection |
| auto | Automatically chooses the strongest supported level | Recommended default |
iOS Security
- iOS Keychain automatically uses hardware-backed encryption when the device supports Secure Enclave
- Hardware backing is transparent and automatic on supported iOS devices
Quick Start
Basic Usage
import SecureStorage from 'rn-secure-keystore';
// Store a value
await SecureStorage.setItem('userToken', 'abc123');
// Retrieve a value
const token = await SecureStorage.getItem('userToken');
// Check if a key exists
const exists = await SecureStorage.hasItem('userToken');
// Remove a value
await SecureStorage.removeItem('userToken');
// Get all keys
const keys = await SecureStorage.getAllKeys();
// Clear all data
await SecureStorage.clear();Biometric Authentication
// Store with biometric protection
await SecureStorage.setItem('sensitiveData', 'secret', {
withBiometric: true,
authenticatePrompt: 'Authenticate to store sensitive data',
authenticatePromptSubtitle:
'Use your fingerprint or face to secure this data',
});
// Retrieve with biometric authentication
const data = await SecureStorage.getItem('sensitiveData', {
authenticatePrompt: 'Authenticate to access sensitive data',
authenticatePromptSubtitle: 'Use your biometric to continue',
});Platform-Specific Features
Android – Advanced Security Options
// Check device capabilities
const capabilities = await SecureStorage.getPlatformCapabilities();
console.log('StrongBox available:', capabilities.hasStrongBox);
// Store with specific security level
await SecureStorage.setItem('data', 'value', {
securityLevel: 'strongbox', // 'strongbox' | 'hardware' | 'software' | 'auto'
allowFallback: true, // Allow fallback if StrongBox not available
});
// Use convenience method for StrongBox
await SecureStorage.setStrongBoxItem('ultraSecure', 'data', true);
// Get recommended security level
const recommendedLevel = await SecureStorage.getRecommendedSecurityLevel();iOS – Keychain Access Control
import SecureStorage, { ACCESS_CONTROL } from 'rn-secure-keystore';
// Store with specific access control (no biometric required for basic storage)
await SecureStorage.setItem('basicData', 'value');
// Store with biometric requirement
await SecureStorage.setItem('biometricData', 'secret', {
accessControl: ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,
authenticatePrompt: 'Authenticate to store data',
});
// Use convenience method
await SecureStorage.setKeychainItem(
'keychainData',
'secret',
ACCESS_CONTROL.BIOMETRY_CURRENT_SET,
'com.yourapp.shared' // access group (optional)
);Security Introspection
// Check individual key security
const isHardwareBacked = await SecureStorage.isKeyHardwareBacked('myKey');
const securityLevel = await SecureStorage.getKeySecurityLevel('myKey');
// Get security status for all keys
const securityStatus = await SecureStorage.getSecurityStatus();
console.log('All keys security:', securityStatus);
// Check if device meets requirements
const requirements = {
requireBiometric: true,
requireHardwareBacking: true,
requireStrongBox: false,
};
const { meets, missing } =
await SecureStorage.meetsSecurityRequirements(requirements);Complete API Reference
Core Storage Methods
setItem(key: string, value: string, options?: StorageOptions): Promise<boolean>
Stores a key-value pair securely with optional biometric protection and security level specification.
Parameters:
key: Unique identifier for the stored valuevalue: Data to store securelyoptions: Optional configuration object
Example:
await SecureStorage.setItem('apiKey', 'sk-123456', {
withBiometric: true,
securityLevel: 'hardware', // Android only
accessControl: ACCESS_CONTROL.BIOMETRY_ANY, // iOS only
});getItem(key: string, options?: GetItemOptions): Promise<string | null>
Retrieves a stored value, prompting for authentication if required.
removeItem(key: string): Promise<boolean>
Removes a stored key-value pair from secure storage.
hasItem(key: string): Promise<boolean>
Checks if a key exists in storage without retrieving the value.
getAllKeys(): Promise<string[]>
Returns an array of all stored keys.
clear(): Promise<boolean>
Clears all secure storage and removes all encryption keys.
Security & Capability Methods
isBiometricAvailable(): Promise<boolean>
Checks if biometric authentication is available and enrolled on the device.
isHardwareBackedAvailable(): Promise<boolean>
Checks if hardware-backed keystore is available on the current device.
isStrongBoxAvailable(): Promise<boolean>
Checks if StrongBox hardware security module is available (Android only).
getHardwareSecurityInfo(): Promise<HardwareSecurityInfo>
Returns comprehensive information about device security capabilities.
const info = await SecureStorage.getHardwareSecurityInfo();
// {
// isHardwareBackedAvailable: true,
// isStrongBoxAvailable: true,
// recommendedSecurityLevel: 'strongbox'
// }getPlatformCapabilities(): Promise<PlatformCapabilities>
Returns detailed platform-specific feature availability.
isKeyHardwareBacked(key: string): Promise<boolean>
Checks if a specific stored key uses hardware-backed security.
getKeySecurityLevel(key: string): Promise<string>
Returns the security level of a specific key ('strongbox', 'hardware', 'software', 'unknown').
getRecommendedSecurityLevel(): Promise<'strongbox' | 'hardware' | 'software'>
Returns the recommended security level for the current device.
getSecurityStatus(): Promise<SecurityStatus>
Returns security information for all stored keys.
meetsSecurityRequirements(requirements): Promise<{meets: boolean, missing: string[]}>
Checks if the current device meets specified security requirements.
Platform-Specific Methods
Android Only
setStrongBoxItem(key: string, value: string, allowFallback?: boolean): Promise<boolean>
Convenience method to store data with StrongBox security.
iOS Only
setKeychainItem(key: string, value: string, accessControl?: string, accessGroup?: string): Promise<boolean>
Convenience method for iOS Keychain with custom access control.
Utility Methods
migrateToSecureStorage(key: string, plainValue: string, options?: StorageOptions): Promise<boolean>
Migrates plain text data to secure storage.
isSecurityLevelAvailable(level: 'strongbox' | 'hardware'): Promise<boolean>
Checks if a specific security level is available on the current device.
Type Definitions
StorageOptions
interface StorageOptions {
withBiometric?: boolean;
// Android-specific options
requireStrongBox?: boolean;
requireHardware?: boolean;
securityLevel?: 'auto' | 'strongbox' | 'hardware' | 'software';
allowFallback?: boolean;
// iOS-specific options
accessGroup?: string | null;
accessControl?: string | null;
// Authentication prompts
authenticatePrompt?: string;
authenticatePromptSubtitle?: string;
}GetItemOptions
interface GetItemOptions {
// iOS-specific
accessGroup?: string | null;
kLocalizedFallbackTitle?: string;
// Android-specific
showModal?: boolean;
// Common
authenticatePrompt?: string;
authenticatePromptSubtitle?: string;
}HardwareSecurityInfo
interface HardwareSecurityInfo {
isHardwareBackedAvailable: boolean;
isStrongBoxAvailable: boolean;
recommendedSecurityLevel: 'strongbox' | 'hardware' | 'software';
}PlatformCapabilities
interface PlatformCapabilities {
platform: string;
hasStrongBox: boolean;
hasHardwareBackedKeystore: boolean;
hasBiometrics: boolean;
hasKeychainAccessControl: boolean;
}Constants
iOS Access Control
import { ACCESS_CONTROL } from 'rn-secure-keystore';
const ACCESS_CONTROL = {
BIOMETRY_ANY: 'kSecAccessControlBiometryAny',
BIOMETRY_CURRENT_SET: 'kSecAccessControlBiometryCurrentSet',
DEVICE_PASSCODE: 'kSecAccessControlDevicePasscode',
APPLICATION_PASSWORD: 'kSecAccessControlApplicationPassword',
BIOMETRY_ANY_OR_DEVICE_PASSCODE:
'kSecAccessControlBiometryAnyOrDevicePasscode',
};Error Codes
import { ERROR_CODES } from 'rn-secure-keystore';
// Authentication errors
ERROR_CODES.AUTHENTICATION_CANCELLED;
ERROR_CODES.AUTHENTICATION_FAILED;
ERROR_CODES.BIOMETRIC_NOT_AVAILABLE;
// Platform errors
ERROR_CODES.PLATFORM_NOT_SUPPORTED;
ERROR_CODES.STRONGBOX_NOT_AVAILABLE;
// Storage errors
ERROR_CODES.STORAGE_ERROR;
ERROR_CODES.RETRIEVAL_ERROR;
// ... and moreError Handling
The library provides comprehensive error handling with detailed error codes:
import { SecureStorageError, ERROR_CODES } from 'rn-secure-keystore';
try {
await SecureStorage.getItem('protectedKey');
} catch (error) {
if (error instanceof SecureStorageError) {
switch (error.code) {
case ERROR_CODES.AUTHENTICATION_CANCELLED:
console.log('User cancelled authentication');
break;
case ERROR_CODES.BIOMETRIC_NOT_AVAILABLE:
console.log('Biometric authentication not available');
break;
case ERROR_CODES.STRONGBOX_NOT_AVAILABLE:
console.log('StrongBox not supported on this device');
break;
default:
console.error('Storage error:', error.message);
}
}
}Best Practices
1. Check Capabilities First
const capabilities = await SecureStorage.getPlatformCapabilities();
if (capabilities.hasStrongBox) {
// Use StrongBox for maximum security
await SecureStorage.setStrongBoxItem('ultraSecret', 'value');
} else if (capabilities.hasHardwareBackedKeystore) {
// Fallback to hardware backing
await SecureStorage.setItem('secret', 'value', { securityLevel: 'hardware' });
}2. Handle Biometric Availability
const biometricAvailable = await SecureStorage.isBiometricAvailable();
if (biometricAvailable) {
await SecureStorage.setItem('biometricData', 'secret', {
withBiometric: true,
});
} else {
// Fallback to device passcode or other authentication
await SecureStorage.setItem('data', 'secret');
}3. Use Appropriate Security Levels
// For highly sensitive data (credit cards, private keys)
await SecureStorage.setItem('creditCard', cardNumber, {
securityLevel: 'strongbox',
withBiometric: true,
allowFallback: true,
});
// For moderately sensitive data (API tokens)
await SecureStorage.setItem('apiToken', token, {
securityLevel: 'hardware',
allowFallback: true,
});
// For less sensitive data (user preferences)
await SecureStorage.setItem('userPrefs', preferences);4. Implement Proper Error Handling
try {
const secret = await SecureStorage.getItem('protectedData', {
authenticatePrompt: 'Access your secure data',
});
} catch (error) {
if (error instanceof SecureStorageError) {
switch (error.code) {
case ERROR_CODES.AUTHENTICATION_CANCELLED:
// Don't show error, user intentionally cancelled
break;
case ERROR_CODES.AUTHENTICATION_FAILED:
showRetryDialog();
break;
default:
showGenericErrorDialog(error.message);
}
}
}5. Audit Security Status
const securityStatus = await SecureStorage.getSecurityStatus();
for (const [key, info] of Object.entries(securityStatus)) {
console.log(
`${key}: Hardware=${info.isHardwareBacked}, Level=${info.securityLevel}`
);
}Platform Differences
Storage Options
| Feature | iOS | Android | Notes | | ----------------- | ------------------------ | ------------------------ | ------------------------------------------------ | | Basic Storage | Hardware-backed keychain | Default Android Keystore | iOS automatically hardware-backed when available | | Hardware Storage | Same as basic | Explicit TEE backing | iOS: automatic, Android: explicit | | StrongBox | Not available | Dedicated security chip | Android-only feature | | Biometric Storage | Face ID/Touch ID | Fingerprint | Platform-native biometric APIs |
Key Differences
iOS:
- Basic storage = Hardware storage (automatically hardware-backed)
- Access control determines authentication requirements
- All keychain items are secure when Secure Enclave is available
Android:
- Hardware ≠ StrongBox (explicit security levels)
- Manual security level control
- StrongBox provides highest security when available
Troubleshooting
Common Issues
"The package doesn't seem to be linked"
- Run
npx pod-install(iOS) - Rebuild your app after installation
- Avoid Expo Go (use development build)
Multiple biometric prompts
- Use single
setItem()call withwithBiometric: true - Don't call
getItem()immediately aftersetItem()for biometric-protected data
"bad base-64" error (Android)
- Corrupted EncryptedSharedPreferences data
- Use
clear()method to clean up corrupted storage - Library automatically handles fallback to regular SharedPreferences
StrongBox not available
- StrongBox requires newer Android devices (API 28+)
- Use
allowFallback: truefor broader compatibility - Check
isStrongBoxAvailable()before using
Authentication errors
- Ensure biometrics are enrolled on device
- Use clear
authenticatePromptmessages - Handle user cancellations gracefully
Debug Information
Enable logging to see detailed storage operations:
// Check device capabilities
const info = await SecureStorage.getHardwareSecurityInfo();
console.log('Security capabilities:', info);
// Check individual key security
const keyInfo = await SecureStorage.isKeyHardwareBacked('myKey');
console.log('Key hardware-backed:', keyInfo);Requirements
- React Native: 0.60+
- iOS: 11.0+
- Android: API level 23+ (Android 6.0+)
Advanced Features
Security Requirements Validation
const requirements = {
requireBiometric: true,
requireHardwareBacking: true,
requireStrongBox: false,
};
const { meets, missing } =
await SecureStorage.meetsSecurityRequirements(requirements);
if (!meets) {
console.log('Missing security features:', missing);
}Migration from Plain Storage
// Migrate existing plain text data to secure storage
await SecureStorage.migrateToSecureStorage('oldKey', plainTextValue, {
securityLevel: 'hardware',
withBiometric: true,
});📄 License
MIT
Contributing
Contributions are welcome! Please open issues and pull requests with clear descriptions.
Support
For issues and questions:
- GitHub Issues: Report bugs and feature requests
- Documentation: Check this README for comprehensive API documentation
- Examples: See the
/examplefolder for complete usage examples
Made with ❤️ for React Native developers who care about security.
