react-native-sensitive-info

April 30, 2026 ยท View on GitHub

npm version npm downloads Coverage License: MIT

Hardware-backed secure storage for React Native. Secrets are encrypted with AES-GCM, gated by biometrics or device credentials, and stored in the system Keychain (iOS/Apple) or Android Keystore โ€” all behind a simple Promise-based API and first-class React hooks.

Note

Upgrading from v5? See MIGRATION.md. v6 requires the New Architecture (RN 0.76+) and react-native-nitro-modules. Windows support was removed.

Table of contents

๐Ÿงญ Platform support

PlatformMinimumNotes
React Native0.76.0New Architecture + react-native-nitro-modules required.
iOS13.0Add NSFaceIDUsageDescription to Info.plist for biometrics.
macOS11.0Catalyst and native macOS via system keychain.
visionOS1.0Secure Enclave-backed; biometric UX adapts to visionOS.
watchOS7.0Paired-device auth; storage syncs through watchOS keychain.
AndroidAPI 23StrongBox requires API 28+; falls back to strongest available authenticator.

โš™๏ธ Installation

npm install react-native-sensitive-info react-native-nitro-modules
# yarn add react-native-sensitive-info react-native-nitro-modules
# pnpm add react-native-sensitive-info react-native-nitro-modules

No manual linking required โ€” Nitro handles autolinking.

๐Ÿ iOS

Run pod install, then add to Info.plist if you use biometric prompts:

<key>NSFaceIDUsageDescription</key>
<string>Face ID is used to unlock secrets stored in the secure enclave.</string>

๐Ÿค– Android

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />

๐Ÿงช Expo

Warning

Expo Go does not include native Nitro modules. Use a custom dev client or an EAS build.

Add the plugin to app.json, then run npx expo prebuild --clean:

{
  "expo": {
    "plugins": ["react-native-sensitive-info"]
  }
}

For a full Expo walkthrough see EXPO.md.

โšก๏ธ Quick start

import { setItem, getItem, deleteItem } from 'react-native-sensitive-info'

// Write โ€” uses the strongest available security policy by default
await setItem('session-token', 'abc123', { service: 'auth' })

// Read โ€” returns value + metadata
const item = await getItem('session-token', { service: 'auth' })
console.log(item?.value)                  // 'abc123'
console.log(item?.metadata.securityLevel) // e.g. 'secureEnclave'

// Remove
await deleteItem('session-token', { service: 'auth' })

Building a component? The hooks API handles loading states, cleanup, and error boundaries โ€” no useEffect boilerplate needed.

import { Text, View, ActivityIndicator } from 'react-native'
import {
  useSecureStorage,
  useSecurityAvailability,
} from 'react-native-sensitive-info/hooks'

function SecretsView() {
  const { items, isLoading, error, saveSecret, removeSecret } =
    useSecureStorage({ service: 'myapp', includeValues: true })
  const { data: capabilities } = useSecurityAvailability()

  if (isLoading) return <ActivityIndicator />
  if (error) return <Text>Error: {error.message}</Text>

  return (
    <View>
      {items.map(item => (
        <Text key={item.key}>
          {item.key}: {item.value} ({item.metadata.securityLevel})
        </Text>
      ))}
      <Text>Biometry: {capabilities?.biometry ? 'available' : 'unavailable'}</Text>
    </View>
  )
}
HookUse case
useSecureStorage()List, add, and remove all secrets in a service.
useSecret()Fetch a single secret with read/write mutations.
useHasSecret()Lightweight existence check (no decryption).
useSecurityAvailability()Query device capabilities (auto-cached).
useKeyRotation()Rotate the master key for a service.
useBiometryStatusWatcher()Subscribe to biometric enrollment-state transitions.

All hooks auto-cancel in-flight requests on unmount. For advanced patterns and best practices see HOOKS.md.

๐Ÿ“š API reference

MethodDescription
setItem(key, value, options?)Write a secret using the strongest available security policy.
getItem(key, options?)Read a secret and its metadata. Pass includeValue: false to skip the payload.
hasItem(key, options?)Check whether a secret exists (no biometric prompt).
deleteItem(key, options?)Remove a secret. Returns true if something was deleted.
getAllItems(options?)List all secrets in a service.
clearService(options?)Remove every secret in a service namespace.
getSupportedSecurityLevels()Snapshot of device capabilities (biometrics, Secure Enclave, StrongBox, โ€ฆ).
canUseAccessControl(policy)Check whether a given access-control policy is supported on this device.
rotateKeys(options?)Bump the active key version; subsequent reads transparently re-encrypt older entries.

Common options:

OptionDefaultDescription
servicebundle ID / 'default'Logical namespace for secrets.
accessControl'secureEnclaveBiometry'Preferred write policy; native layer picks the strongest supported fallback.
authenticationPromptโ€”Localized strings for biometric / device-credential prompts.
iosSynchronizablefalseSync via iCloud Keychain.
keychainGroupโ€”Custom Keychain access group.

Errors are typed โ€” import predicates from react-native-sensitive-info/errors:

import {
  isAuthenticationCanceledError,
  isKeyInvalidatedError,
  isIntegrityViolationError,
} from 'react-native-sensitive-info/errors'

๐Ÿ›ก๏ธ Security

Every secret is encrypted with AES-GCM and bound to a hardware-protected key โ€” the Secure Enclave on Apple platforms and the Android Keystore (StrongBox when available). Each entry carries an HMAC-SHA256 integrity tag recomputed on every read; a mismatch raises IntegrityViolationError before any biometric prompt fires, so spoofed entries can never trigger user authentication.

For the full cryptographic model โ€” key derivation, AAD binding, replay defense, and threat classification โ€” see THREAT_MODEL.md.

๐ŸŒณ Tree-shaking

All entry points are side-effect-free ("sideEffects": false). Import only what you use:

SubpathContents
react-native-sensitive-infoCore API (setItem, getItem, hasItem, deleteItem, getAllItems, clearService, rotateKeys, โ€ฆ)
react-native-sensitive-info/hooksReact hooks (useSecureStorage, useSecret, useKeyRotation, โ€ฆ)
react-native-sensitive-info/errorsTyped error classes and instanceof predicates

๐ŸŽฎ Example app

cd example && yarn install

yarn ios      # iOS
yarn android  # Android

๐Ÿ› ๏ธ Development

yarn install    # Install dependencies
yarn codegen    # Regenerate Nitro bindings
yarn typecheck  # Type-check sources
yarn build      # Build distributable packages

๐Ÿฉบ Troubleshooting

  • Biometric prompt never appears โ€” use canUseAccessControl(policy) to check before writing and fall back to devicePasscode if needed.
  • authentication failed on simulator โ€” Secure Enclave and StrongBox are unavailable on simulators. Validate biometric policies on physical hardware.
  • E_AUTH_CANCELED in error message โ€” the user dismissed the prompt. Handle gracefully; hook state is not poisoned by cancellations.
  • Undefined symbol on iOS โ€” re-run pod install after upgrading to v6.

๐Ÿค Contributing

Contributions are welcome!

  • ๐Ÿ› Bugs โ€” Open an issue with a minimal reproduction. Security vulnerabilities should be reported privately โ€” see SECURITY.md.
  • ๐Ÿ’ก New features โ€” Open an issue first to align on scope before writing code.
  • ๐Ÿ”€ Pull requests โ€” Run yarn test and yarn typecheck before submitting. Keep changes focused and reference the relevant issue.

Please review CODE_OF_CONDUCT.md before contributing.

๐Ÿ“„ License

MIT ยฉ Mateus Andrade