{"_id":"@beatsphere/expo-secure-token-manager","name":"@beatsphere/expo-secure-token-manager","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@beatsphere/expo-secure-token-manager","version":"0.1.0","description":"Resilient wrapper around expo-secure-store with retry logic for iOS Keychain and Android Keystore errors","main":"build/index.js","types":"build/index.d.ts","scripts":{"build":"tsc","prepublishOnly":"tsc","clean":"rm -rf build"},"keywords":["expo","react-native","secure-store","keychain","keystore","token","credentials","ios","android","retry"],"author":{"name":"BeatSphere","email":"hello@beatsphere.live"},"license":"MIT","homepage":"https://github.com/Beatsphere/expo-secure-token-manager#readme","repository":{"type":"git","url":"git+https://github.com/Beatsphere/expo-secure-token-manager.git"},"bugs":{"url":"https://github.com/Beatsphere/expo-secure-token-manager/issues"},"peerDependencies":{"expo-secure-store":">=13.0.0","react-native":">=0.74.0"},"devDependencies":{"expo-secure-store":"^14.0.0","react-native":"^0.76.0","typescript":"^5.0.0"},"gitHead":"bdf73dddaf1b037cc3e3b425cab81749c74161b5","_id":"@beatsphere/expo-secure-token-manager@0.1.0","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-jdKk82+X3rZP7SpMJf8AU3fdNID8sM6DSu6EvUuRnd807kVPK+SPkXw7Tg2PemGkxjeGMEj1XvmJuvPo8qhKFg==","shasum":"1cad67dc82e60d00a8b1804fe78baeadc471e32b","tarball":"https://registry.npmjs.org/@beatsphere/expo-secure-token-manager/-/expo-secure-token-manager-0.1.0.tgz","fileCount":8,"unpackedSize":21653,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCJ0JculJiGvykuNXKuk9xSiNy4e+g51K5DrkoguiJMQgIgLUOBqZ9gbgdwfsLSAK4Uo+RemQXYpwf/Iv2ZKUuh4qU="}]},"_npmUser":{"name":"ayushh2k","email":"rustichouse042@gmail.com"},"directories":{},"maintainers":[{"name":"ayushh2k","email":"rustichouse042@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/expo-secure-token-manager_0.1.0_1776970955547_0.46709260371325056"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-23T19:02:35.450Z","0.1.0":"2026-04-23T19:02:35.682Z","modified":"2026-04-23T19:02:35.908Z"},"maintainers":[{"name":"ayushh2k","email":"rustichouse042@gmail.com"}],"description":"Resilient wrapper around expo-secure-store with retry logic for iOS Keychain and Android Keystore errors","homepage":"https://github.com/Beatsphere/expo-secure-token-manager#readme","keywords":["expo","react-native","secure-store","keychain","keystore","token","credentials","ios","android","retry"],"repository":{"type":"git","url":"git+https://github.com/Beatsphere/expo-secure-token-manager.git"},"author":{"name":"BeatSphere","email":"hello@beatsphere.live"},"bugs":{"url":"https://github.com/Beatsphere/expo-secure-token-manager/issues"},"license":"MIT","readme":"# @beatsphere/expo-secure-token-manager\n\nResilient wrapper around `expo-secure-store` with automatic retry logic for iOS Keychain and Android Keystore errors. Battle-tested in [BeatSphere](https://beatsphere.live).\n\n## Why\n\n`expo-secure-store` throws on several real-world edge cases:\n\n- **iOS**: `\"User interaction is not allowed\"` when accessing the Keychain during background execution or while the device is locked\n- **Android**: `\"Could not encrypt/decrypt\"` when the Keystore encryption key becomes corrupted (happens on some devices after OS updates or factory reset recovery)\n\nThis wrapper catches these errors, retries with backoff, and on Android write failures, deletes the corrupted key to force the Keystore to create a fresh encryption key.\n\n## Install\n\n```bash\nnpm install @beatsphere/expo-secure-token-manager\n```\n\n## Usage\n\n```typescript\nimport {\n  getSecureItem,\n  setSecureItem,\n  deleteSecureItem,\n} from '@beatsphere/expo-secure-token-manager';\n\n// Store a token — returns true on success, false on failure\nawait setSecureItem('auth_token', 'eyJhbGciOiJIUzI1NiIs...');\n\n// Retrieve a token — returns null on failure instead of throwing\nconst token = await getSecureItem('auth_token');\n\n// Delete a token\nawait deleteSecureItem('auth_token');\n```\n\n### Configuration (optional)\n\n```typescript\nimport { configure } from '@beatsphere/expo-secure-token-manager';\nimport * as SecureStore from 'expo-secure-store';\n\nconfigure({\n  maxRetries: 5,           // default: 3\n  retryDelay: 200,         // default: 100ms (multiplied by attempt number)\n  keychainAccessible: SecureStore.WHEN_UNLOCKED, // default: AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY\n});\n```\n\n## API\n\n| Function | Returns | Description |\n|----------|---------|-------------|\n| `configure(opts)` | `void` | Set retry and Keychain options |\n| `getSecureItem(key)` | `Promise<string \\| null>` | Get value with retry. Returns `null` on failure |\n| `setSecureItem(key, value)` | `Promise<boolean>` | Set value with retry. On Android encryption error, deletes corrupted key and retries |\n| `deleteSecureItem(key)` | `Promise<boolean>` | Delete value. Returns `true` even on error |\n\n## How retry works\n\n### Read (`getSecureItem`)\n1. Attempt `SecureStore.getItemAsync`\n2. On Android decryption error → retry (does NOT delete, that would destroy the value)\n3. On iOS Keychain error → retry\n4. After max retries → return `null`\n\n### Write (`setSecureItem`)\n1. Attempt `SecureStore.setItemAsync`\n2. On Android encryption error → **delete the key** (resets corrupted encryption state) → retry\n3. On iOS Keychain error → retry\n4. After max retries → return `false`\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-6aed73f7d2cb0f15e88981b32b061a42"}