Skip to content

Releases: mCodex/react-native-sensitive-info

v6.1.4

29 Apr 18:18

Choose a tag to compare

🔐 react-native-sensitive-info v6.1.4

🐛 iOS Biometric Prompt Fix

This release fixes duplicate Face ID / Touch ID prompts on iOS when working with biometric-protected Keychain entries.

✨ What Changed

  • 🧠 Prompted value reads now own an LAContext from the first SecItemCopyMatching call.
  • 🤫 hasItem now uses a dedicated silent existence path on iOS.
  • 🔎 Metadata-only reads and listings avoid triggering biometric UI.
  • 🧩 JS option normalization is now operation-aware, so silent probes do not accidentally forward biometric prompt fields.
  • 🪝 Hooks now avoid leaking prompt-bearing options into silent fetch/existence checks.

✅ Fixed Scenarios

  • Calling hasItem() before getItem() no longer causes two Face ID prompts.
  • useHasSecret() remains silent even when callers reuse biometric option bags.
  • useSecureStorage() metadata listings stay prompt-free unless values are explicitly requested.
  • getItem(..., { includeValue: false }) avoids biometric prompts for metadata-only reads.
  • Prompted getItem() reads avoid retry-driven duplicate Face ID / Touch ID sheets.

🚀 Why This Matters

On iOS, even Keychain existence or attribute queries can require user presence for biometric-protected items. Previously, an app flow like:

await hasItem('session-token', { service: 'auth' })
await getItem('session-token', {
  service: 'auth',
  authenticationPrompt: { title: 'Unlock session' },
})

could show Face ID twice: once during the existence check and once during the actual unlock.

Now, silent probes stay silent, and authentication is reserved for the operation that actually opens the protected value.

📱 Recommended iOS Validation

For apps using biometric-protected secrets, validate on a real iOS device:

  1. Store an item with biometric access control.
  2. Call hasItem() during initialization.
  3. Call getItem() when the user explicitly unlocks.
  4. Confirm there is no Face ID prompt during initialization and exactly one prompt during unlock.

🙌 Upgrade Notes

No public API changes are required. Existing apps should benefit automatically, especially flows that perform existence checks before reading a protected value.

v6.1.3

29 Apr 13:23

Choose a tag to compare

🛠️ v6.1.3 — Release Pipeline & Bundler Compatibility Fixes

TL;DRv6.1.2 shipped a broken tarball: it was missing all nitrogen/generated/ native bindings (breaking pod install with cannot load such file -- nitrogen/generated/ios/SensitiveInfo+autolinking.rb) and its exports map could not be resolved by some bundlers (Re.Pack/rspack), causing Cannot find module 'react-native-sensitive-info/hooks' at build time.

v6.1.3 is a pure release-tooling fix — no runtime/API changes. Upgrade and rebuild.


🚑 Bug Fixes

📦 Restore missing iOS / Android native bindings in the published tarball

v6.1.2 was published without any nitrogen/generated/ output because:

  • nitrogen/ is gitignored (it's a build artifact).
  • The previous release-it before:init hook only ran typecheck + bob build, not nitrogen codegen.
  • So when CI checked out a clean tree, the nitrogen/ directory never existed at publish time.

Symptom on consumer side:

[!] Invalid Podfile file: [!] Invalid SensitiveInfo.podspec file:
cannot load such file -- nitrogen/generated/ios/SensitiveInfo+autolinking.rb.

Fix: the release pipeline now always regenerates nitrogen artifacts (via npm run codegen) before packing/publishing, and aborts the release if any expected artifact is missing.

🔌 Fix Cannot find module 'react-native-sensitive-info/hooks' under Re.Pack / rspack

Some bundlers (notably Re.Pack/rspack, and certain Metro configurations) don't resolve the package exports map for subpath imports, so import { useSecret } from 'react-native-sensitive-info/hooks' failed at build time even though the files were physically present in node_modules.

Fix: added classic subpath proxy directories (hooks/, errors/) following the same pattern used by date-fns, lodash, react-router, and @reduxjs/toolkit. They expose main / module / react-native / types fields pointing into lib/..., so any bundler that falls back to filesystem resolution can find them. Modern bundlers continue to use the exports map — both paths resolve to the same files.

// hooks/package.json (shipped in the tarball)
{
  "main":         "../lib/commonjs/hooks/index.js",
  "module":       "../lib/module/hooks/index.js",
  "react-native": "../lib/module/hooks/index.js",
  "types":        "../lib/typescript/commonjs/src/hooks/index.d.ts",
  "sideEffects":  false
}

✅ Release Pipeline Hardening

To make sure this kind of regression can never ship again, release-it's before:init hook now runs a single release:prepare script that performs three sequential checks. Any failure aborts the publish + git push.

# Step What it guarantees
1 🏗️ npm run codegen Regenerates nitrogen/ (iOS/Android autolinking, shared C++ headers) and runs bob build.
2 🔍 verify-release-artifacts.js Fast filesystem check for 11 critical artifacts in the workspace. Fails fast with an actionable message.
3 🚀 smoke-test-release.js Full end-to-end consumer simulation: npm pack → install into a sandbox → resolve every documented subpath via both the exports map and the legacy proxy fields → ruby -c on the podspec + autolinking.rb to catch parse errors.

If v6.1.2 had been gated by this pipeline, the release would have been blocked at step 3 with a clear error pointing at the missing nitrogen/generated/ios/SensitiveInfo+autolinking.rb.


🧰 What you need to do

Just bump the version — no code changes are required:

npm install react-native-sensitive-info@^6.1.3
# then on iOS:
cd ios && pod install

If you were stuck on v6.1.2 due to the pod install or Cannot find module 'react-native-sensitive-info/hooks' errors, both are resolved.


📁 Files changed

  • package.json — added release:prepare script; before:init now runs it; hooks/ and errors/ added to files.
  • hooks/package.json (new) — subpath proxy for bundlers without exports support.
  • errors/package.json (new) — subpath proxy for bundlers without exports support.
  • scripts/verify-release-artifacts.js (new) — workspace-level artifact check.
  • scripts/smoke-test-release.js (new) — end-to-end pack/install/resolve smoke test.

v6.1.2

29 Apr 13:06

Choose a tag to compare

🚀 v6.1.2

A focused patch release that unblocks Re.Pack / rspack consumers, hardens the new cause property to fully match native ES2022 Error semantics, and ships a few CI hygiene fixes. No breaking changes — drop-in upgrade. 🎉


🐛 Bug Fixes

📦 Re.Pack / rspack: Cannot find module 'react-native-sensitive-info/hooks' (and /errors)

Consumers using Re.Pack / rspack (instead of Metro) were hitting:

[runtime not ready]: Error: Cannot find module 'react-native-sensitive-info/hooks'
  __rspack_missing_module
  ./src/hooks/useAppInitializer.tsx

🔍 Root cause: the subpath exports in package.json declared a "react-native" condition that pointed at raw TypeScript source (./src/hooks/index.ts). Metro transpiles node_modules so it worked there, but Re.Pack / rspack don't transpile library source by default, so resolution failed at bundle time.

✅ Removed the "react-native" condition from the ./, ./hooks, and ./errors subpath exports — now matching the convention used by react-native-mmkv, react-native-reanimated, and @react-native-async-storage/async-storage. The top-level "react-native": "src/index" and "source": "src/index" fields are kept so Metro still gets source-map debugging for the main entry.

📂 Changed: package.json


🔒 Error API: full ES2022 cause parity

Building on the type-compatibility fix from v6.1.1, this release polishes SensitiveInfoError and HookError so their cause property is indistinguishable from a native ES2022 Error.

cause is now non-enumerable

Previously assigned as a regular property (which would have made it enumerable, surface in Object.keys, and leak into JSON.stringify). Now installed via Object.defineProperty(this, 'cause', { writable: true, configurable: true, enumerable: false, ... }) — exactly how the native Error constructor does it.

✅ Logging, serialization, and DevTools error overlays no longer accidentally include the cause chain.

🧬 { cause: undefined } is now honored

Native new Error(msg, { cause: undefined }) still installs an own (non-enumerable) cause property. Both error classes now mirror that:

  • SensitiveInfoError already used 'cause' in options
  • HookError was checking cause !== undefined, which silently dropped the property — now also uses 'cause' in options

🏷️ Type-only cause declaration for older TS targets

Added declare readonly cause?: unknown on both classes so consumers compiling with a tsconfig lib that predates ES2022 can still reference err.cause without TS errors. The declare form is fully erasable — no class field is emitted, so it can't accidentally become an enumerable own property.

📂 Changed: src/errors.ts, src/hooks/types.ts


🧪 Tests

Added thorough coverage in src/tests/errors.test.ts and src/tests/hooks.types.test.ts:

  • cause is retained on the constructed error
  • cause is non-enumerable (descriptor check, hidden from Object.keys, hidden from JSON.stringify)
  • cause is not defined when no options are provided
  • cause is defined as undefined when explicitly passed ({ cause: undefined }) — matching native Error
  • cause propagates through subclasses (NotFoundError, etc.)

48 tests, all green. 💚


🛠️ Other Changes

🌿 CI workflow triggers aligned with the default branch

The repo's default branch is master, but test.yml, ios-build.yml, and android-build.yml were filtering push events on main, so those workflows weren't running on push to the actual default branch.

✅ Updated all three workflows to trigger on master.

📂 Changed: .github/workflows/test.yml, .github/workflows/ios-build.yml, .github/workflows/android-build.yml


⬆️ Upgrade

No code changes required — just bump:

yarn add react-native-sensitive-info@6.1.2
# or
npm install react-native-sensitive-info@6.1.2

If you're using Re.Pack / rspack and were stuck on a workaround for the Cannot find module 'react-native-sensitive-info/hooks' error, you can now remove that workaround.

v6.1.1

29 Apr 12:28

Choose a tag to compare

🚀 v6.1.1

A small but important patch release that unblocks TypeScript builds for downstream consumers and modernizes the release pipeline. No API changes, no migration steps. 🎉


🐛 Bug Fixes

🛠️ Fixed TS2554 when compiling against older lib targets

Consumer projects whose tsconfig doesn't include the ES2022 lib were failing to type-check against our shipped src/ with:

node_modules/react-native-sensitive-info/src/errors.ts:75:18 - error TS2554: Expected 0-1 arguments, but got 2.
node_modules/react-native-sensitive-info/src/hooks/types.ts:41:18 - error TS2554: Expected 0-1 arguments, but got 2.

SensitiveInfoError and HookError no longer pass the { cause } options object to super(). The cause is now assigned as a property after construction, so the library type-checks cleanly under any tsconfig lib target.

🔒 Runtime behavior is unchangederror.cause is still populated when provided.

📂 Changed files: src/errors.ts, src/hooks/types.ts


🛠️ Other Changes

🔁 Migrated release tooling: semantic-releaserelease-it

Consolidated to a single, conventional-OSS release flow that mirrors what most modern React Native libraries use:

  • 🗑️ Removed semantic-release, @semantic-release/changelog, @semantic-release/git, and conventional-changelog-cli.
  • ✨ Added @release-it/conventional-changelog for automatic version bumps and CHANGELOG.md generation from conventional commits — preserving the previous emoji section grouping:
    • ✨ Features
    • 🐛 Bug Fixes
    • 💨 Performance Improvements
    • 🔄 Code Refactors
    • 📚 Documentation
    • 🛠️ Other changes
  • 📦 release-it config now lives in package.json (no more separate .release-it.json / release.config.cjs).
  • 🏷️ Single entry point: npm run release (the release:it script was removed).
  • 🤖 GitHub Actions Release workflow rewritten to run release-it --ci with optional workflow_dispatch inputs:
    • increment: patch / minor / major / prerelease
    • dry-run: preview without publishing
    • Uses a bot git identity and enables npm provenance.
  • 🧹 ~280 transitive dependencies removed (~11 MiB smaller lockfile).

⬆️ Upgrade

No code changes required — just bump:

yarn add react-native-sensitive-info@6.1.1
# or
npm install react-native-sensitive-info@6.1.1

v6.1.0

28 Apr 20:19

Choose a tag to compare

🔐 react-native-sensitive-info v6.1.0

A focused follow-up to the v6 GA: smarter biometric capability detection, two production bug fixes that eliminate double biometric prompts, and a few DX/build polish items. Fully backward-compatible — apps reading only the biometry boolean keep working unchanged.


✨ What's new

👁️ Fine-grained biometric availability

A single biometry: boolean couldn't tell the difference between "no hardware", "hardware present but the user hasn't enrolled", and "ready to use" — three UX-distinct states. The new biometryStatus field on SecurityAvailability disambiguates them:

type BiometryStatus =
  | 'available'      // ✅ enrolled and usable right now
  | 'notEnrolled'    // 🟡 hardware OK, no fingerprint/face registered yet
  | 'notAvailable'   // 🚫 no hardware / admin-disabled / passcode unset
  | 'lockedOut'      // ⏳ too many failed attempts
  | 'unknown'        // ❓ probe could not classify

Drive a "Set up Face ID" CTA off 'notEnrolled' instead of hiding the toggle. Mapped natively from LAError codes on iOS and BiometricManager.canAuthenticate results on Android.

Invariant: biometry === (biometryStatus === 'available').

🛡️ Policy precheck — canUseAccessControl

Predict whether a future setItem write with a given AccessControl policy will succeed before you try and trigger a prompt:

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

if (await canUseAccessControl('secureEnclaveBiometry')) {
  await setItem('session', token, { accessControl: 'secureEnclaveBiometry' })
} else {
  await setItem('session', token, { accessControl: 'devicePasscode' })
}

Pass a snapshot you already hold to skip the native call entirely:

import { canUseAccessControlSync } from 'react-native-sensitive-info'
import { useSecurityAvailability } from 'react-native-sensitive-info/hooks'

const { data: caps } = useSecurityAvailability()
const canEnable = caps ? canUseAccessControlSync('secureEnclaveBiometry', caps) : false

🔁 Foreground auto-refresh

Users commonly leave the app to enroll a fingerprint and come back — your toggle should catch up automatically:

const { data: caps } = useSecurityAvailability({ refreshOnForeground: true })

Subscribes to AppState only when enabled, debounces back-to-back active transitions (~500 ms), and unsubscribes on unmount.

🔔 Enrollment listener — useBiometryStatusWatcher

Transition-only callback (fires once per real BiometryStatus change, never on every render):

import { useBiometryStatusWatcher } from 'react-native-sensitive-info/hooks'

useBiometryStatusWatcher((next, previous) => {
  if (previous === 'notEnrolled' && next === 'available') {
    showToast('Face ID is ready.')
  }
})

Lives in its own module so apps that don't watch enrollment changes don't pay for it (sideEffects: false keeps tree-shaking honest).


🐛 Bug fixes

📱 iOS — no more double Face ID / Touch ID prompt on getItem

The lazy re-encryption path that runs after a successful authenticated read used to call SecItemUpdate against the same Keychain item to refresh its key-version metadata; iOS treats that as a separate authorization gate, prompting the user a second time. Biometric items now skip the lazy refresh entirely and are upgraded only by an explicit setItem or rotateKeys({ reEncryptEagerly: true }). Non-biometric items continue to be upgraded silently.

🤖 Android — same double-prompt regression, fixed

Lazy re-encryption inside getItem allocated a new key alias for the active version and Cipher.init on that fresh setUserAuthenticationRequired(true) key required its own biometric authorization, surfacing as a second prompt right after the read. The lazy refresh now skips entries with requiresAuthentication == true (and any biometry-class access policy).

🪪 iOS — errSecDuplicateItem on setItem, fixed

setItem no longer fails with "The specified item already exists in the keychain" when:

  • the caller toggles iosSynchronizable between writes, or
  • iCloud Keychain restores an entry between our delete and add.

The internal upsert helper now wipes prior entries with kSecAttrSynchronizableAny and absorbs the iCloud-restore race with a single bounded retry. Bundle ID + access group already scope the partition, so the overwrite never crosses an app or sharing boundary.


🧰 Tooling & DX

  • ⚛️ babel-plugin-react-compiler now actually runs on the published bundle. The react-native-builder-bob targets opt into configFile: true so they pick up the library's babel.config.js, which branches on the bob caller and pairs the compiler with the right preset.
  • 🚦 Four hooks that coordinate refs across renders (useStableOptions, useAsync, useMutation, useSecureStorage) carry an explicit 'use no memo' opt-out — the compiler can't preserve their structural-stability guarantees and now skips them deliberately.

📚 Docs

  • Clarify SecurityAvailability.secureEnclave semantics across platforms (Secure Enclave on iOS / mirrors strongBox on Android) — gate "hardware-backed key" UX without branching on Platform.OS.
  • README + CHANGELOG correctly state that canUseAccessControl(policy, levels?) only skips the native call when a snapshot is supplied.
  • Android requiresBiometricAuth doc comment now matches the actual classification.

🧨 Breaking changes

None. 🎉


🙌 Upgrade

npm install react-native-sensitive-info@6.1.0
# or
yarn add react-native-sensitive-info@6.1.0

Then re-run pod install from ios/ and rebuild.

Full diff: v6.0.0...v6.1.0

v6.0.0

28 Apr 15:35

Choose a tag to compare

🔐 v6.0.0 — Release Notes (5.6.2 → 6.0.0) 🚀

react-native-sensitive-info 6 is a from-scratch rewrite on top of Nitro Modules and the React Native New Architecture. It is not a drop-in upgrade from 5.6.2 — the API surface is intentionally narrower, fully typed, and metadata-rich. Plan a migration window and use the migration guide.

⚡ TL;DR

  • New runtime: Nitro hybrid object replaces the legacy bridge module. Requires React Native ≥ 0.80 with the New Architecture enabled.
  • Promise-based API throughout; every read/write returns rich StorageMetadata.
  • Typed errors (SensitiveInfoError subclasses) with instanceof predicates — no more string-matching.
  • First-class React hooks under react-native-sensitive-info/hooks.
  • Versioned key rotation with lazy re-encryption.
  • Defense-in-depth hardening: HMAC integrity tag, AES-GCM AAD binding, setUnlockedDeviceRequired, plaintext zeroization, constant-time comparisons.
  • Tree-shakeable subpath exports (., /hooks, /errors); "sideEffects": false.
  • Windows is no longer supported. v6 targets Android + Apple platforms (iOS, macOS, visionOS, watchOS).
  • First-class Expo config plugin — Face ID usage description, biometric permissions, and New Architecture flags wired up automatically. Expo Go is not supported; use a Dev Client or EAS Build.

🤔 Why upgrade?

5.6.2 6.0.0
Runtime Legacy bridge module (Paper-compatible) Nitro hybrid object (New Architecture only)
Architecture Old + Fabric (bridge fallback) New Architecture required
API surface Mixed sync/async, manual flag soup (kSecAccessControl*, keystore) Promise-based, typed, single accessControl enum
Return types void writes, raw value reads MutationResult writes, SensitiveInfoItem | null reads with metadata
Errors Plain Error instances, message string-matching Typed subclasses + is*Error predicates, dedicated /errors subpath
Metadata None securityLevel, backend, accessControl, keyVersion, integrityTag, timestamp
Key rotation Not available rotateKeys() + getKeyVersion() with lazy / eager re-encryption
Integrity AES-GCM tag only AES-GCM tag + HMAC-SHA256 over (service, key, version, accessControl, securityLevel, timestamp, ciphertext, iv)
Replay/swap defense None AES-GCM AAD bound to service|key|v<version> (Android); kSecAttrService + kSecAttrAccount (iOS)
Plaintext lifetime Best-effort Zeroized on both platforms after encrypt/decrypt
React hooks None useSecret, useSecretItem, useHasSecret, useSecureStorage, useSecurityAvailability, useKeyRotation, useSecureOperation
Expo support None / community plugin First-party app.plugin.js (Info.plist, AndroidManifest, new-arch flags)
Windows support Yes (legacy) Removed
Bundle size Single CJS entry "sideEffects": false, subpath exports, no default export
Lint/format toolchain ESLint + Prettier Biome 2 (single config)
TypeScript Loose typings Strict, exactOptionalPropertyTypes, declaration maps

✨ Highlights

🏎️ Nitro hybrid core (RN 0.80+ / New Architecture)

The core moves from the legacy module bridge to a Nitro hybrid object. Native calls bypass the JS bridge serialization layer, so secure-storage operations run with significantly lower marshalling overhead and predictable latency.

  • iOS / Apple platforms: Swift + CryptoKit + Keychain, Secure Enclave-gated AES-GCM.
  • Android: Kotlin + Keystore (StrongBox-aware) with EncryptedSharedPreferences software fallback.

📦 Promise-based API with metadata

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

const result = await setItem('session-token', 'super-secret', {
  service: 'auth',
  accessControl: 'secureEnclaveBiometry',
})
// result.metadata: { securityLevel, backend, accessControl, keyVersion, integrityTag, ... }

const item = await getItem('session-token', { service: 'auth' })
// item: { key, service, value?, metadata } | null

Every read/write returns StorageMetadata, so apps can confirm the actual security level applied (e.g. refuse to store secrets when metadata.securityLevel === 'software').

🚨 Typed errors

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

try {
  await getItem('token', { service: 'auth' })
} catch (error) {
  if (isAuthenticationCanceledError(error)) return
  if (isKeyInvalidatedError(error)) {
    // Hardware key invalidated (e.g. biometrics re-enrolled)
  }
  throw error
}

Predicates: isNotFoundError, isAuthenticationCanceledError, isIntegrityViolationError, isKeyInvalidatedError, isRotationFailedError, isInvalidArgumentError.

⚛️ React hooks

A focused hooks API ships at react-native-sensitive-info/hooks:

Hook Use case
useSecureStorage List + CRUD over a service
useSecret Single secret + save/delete
useSecretItem Single secret read
useHasSecret Lightweight existence check
useSecurityAvailability Device capability snapshot
useKeyRotation Bump and inspect master-key version
useSecureOperation Wrap arbitrary imperative calls in a state machine

All hooks share the same lifecycle/abort/error pipeline (useAsyncQuery, useMutation) and never tear state on unmount.

🔁 Versioned key rotation

import { rotateKeys, getKeyVersion } from 'react-native-sensitive-info'

await rotateKeys({ service: 'auth' })                    // lazy, upgrades on next read
await rotateKeys({ service: 'auth', reEncryptEagerly: true }) // eager
const version = await getKeyVersion({ service: 'auth' })
  • iOS: SecItemUpdate preserves existing access-control attributes while bumping keyVersion.
  • Android: mints a fresh per-entry Keystore alias (SensitiveInfo_<hash>_v<version>) and deletes the stale one after a successful rewrite.

🛡️ Defense-in-depth hardening

Applied transparently to new writes and via lazy upgrade on rotation. Backwards compatible — entries written by older 6.x RC builds decode without verification and are upgraded on the next write or rotation.

  • HMAC-SHA256 integrity tag bound to every entry's metadata + ciphertext (StorageMetadata.integrityTag). Tampering raises IntegrityViolationError before any biometric prompt is shown.
  • AES-GCM AAD on Android binds ciphertext to service|key|v<version>, defeating cross-entry swap attacks.
  • setUnlockedDeviceRequired(true) on every Android Keystore key (API 28+), mirroring iOS kSecAttrAccessibleWhenUnlocked semantics.
  • Plaintext byte buffers zeroized after use on both platforms.
  • Constant-time HMAC comparison (MessageDigest.isEqual / manual UInt8 XOR fold).

🌳 Tree-shaking & ESM-first packaging

"sideEffects": false everywhere, with three focused subpath entries:

Import Contents
react-native-sensitive-info setItem, getItem, hasItem, deleteItem, getAllItems, clearService, getSupportedSecurityLevels, rotateKeys, getKeyVersion, types
react-native-sensitive-info/hooks Every React hook
react-native-sensitive-info/errors Typed error classes + instanceof predicates

The default export is gone — use named imports.

🧩 Expo config plugin

A first-party app.plugin.js ships in the package. Add it to app.json / app.config.ts:

{
  "expo": {
    "plugins": [
      ["react-native-sensitive-info", {
        "faceIDPermission": "Authenticate to unlock your account.",
        "enableNewArchitecture": true
      }]
    ]
  }
}

Handles: NSFaceIDUsageDescription, USE_BIOMETRIC + legacy USE_FINGERPRINT (with maxSdkVersion=28), and New Architecture flags (newArchEnabled, RCT_NEW_ARCH_ENABLED). See docs/EXPO.md.

💥 Breaking changes

A migration table for the most common call sites:

5.6.2 6.0.0
Bridge module, Old Architecture compatible Nitro hybrid object, New Architecture required
setItem(key, value, options) returns void setItem(key, value, options) returns Promise<MutationResult>
getItem(key, options) returns the raw value or null getItem(key, options) returns SensitiveInfoItem | null ({ key, service, value, metadata })
getAllItems(options) returns Record<string, string> getAllItems(options) returns SensitiveInfoItem[]
deleteItem(key, options) returns void deleteItem(key, options) returns Promise<boolean>
sharedPreferencesName (Android) / keychainService (iOS) unified as service
kSecAccessControl* strings + Android keystore config object single accessControl: 'secureEnclaveBiometry' | 'biometryCurrentSet' | 'biometryAny' | 'devicePasscode' | 'none'
setInvalidatedByBiometricEnrollment opt-out Always-on; biometric-bound entries raise KeyInvalidatedError deterministically
Errors are plain Error instances Typed SensitiveInfoError subclasses + is*Error predicates
Default + named exports from package root Named exports only; no default export; hooks moved to /hooks
Windows supported Windows removed

🔑 Access-control flag mapping

5.6.2 (kSecAccessControl…) 6.0.0 (accessControl)
kSecAccessControlBiometryCurrentSet + Secure Enclave class secureEnclaveBiometry
kSecAccessControlBiometryCurrentSet biometryCurrentSet
kSecAccessControlBiometryAny biometryAny
kSecAccessControlDevicePasscode devicePasscode
(no policy) none

🚚 Data migrati...

Read more

v6.0.0-rc.12

16 Dec 15:08

Choose a tag to compare

v6.0.0-rc.12 Pre-release
Pre-release
  • Refactor code structure for improved readability and maintainability (e515a9d)
  • fix: update repository field format in package.json (eeadcb8)
  • Merge pull request #532 from mCodex/feat/androidBiometric14 (0c7796a)
  • Make HybridSensitiveInfo class and methods public (5bdbb63)
  • Update Android biometric lib and improve null handling (9023957)
  • feat: restructure app components and implement secure storage functionality (b84ec82)

What's Changed

Full Changelog: v6.0.0-rc.9...v6.0.0-rc.12

v6.0.0-rc.9

03 Nov 15:14

Choose a tag to compare

v6.0.0-rc.9 Pre-release
Pre-release

Changelog:

  • chore: normalize formatting in hooks (semicolons/spacing) and bump example iOS Podfile.lock deps (c9087e6)
  • Merge pull request #494 from mCodex/refactor/appExample (ed1b003)
  • docs: add error-handling section to README and introduce SECURITY.md (69876ac)
  • fix(auth): treat authentication cancellations as soft-failures and map native cancel codes (4454883)
  • chore: bump dev/example deps and normalize README version notation (0c6bd68)
  • Update GitHub Sponsors username in FUNDING.yml (c495d01)
  • example: overhaul App.tsx to use hooks-based playground UI (4d3540f)

Release 5.6.2

03 Nov 14:13

Choose a tag to compare

5.6.2 (2025-11-03)

  • chore: bump devDependencies and regenerate lockfiles (006236a)
  • Refactor entry point and enhance native fallback logic (91916b1)
  • Update src/index.ts (b27dda4)
  • Update src/index.ts (04aece9)

What's Changed

  • Refactor entry point and enhance native fallback logic #485 by @mCodex in #489

Full Changelog: v5.6.1...v5.6.2

Release 5.6.1

29 Oct 12:00

Choose a tag to compare

5.6.1 (2025-10-29)

Fixes [#480 ]

  • Refactor iOS access control and Keychain handling; add RN Swift module & ObjC bridge; update deps an (4ccb7f1)

Full Changelog: v5.6.0...v5.6.1