React Native Secure Token Storage vs AsyncStorage for Auth Sessions
Quick answer
AsyncStorage is unencrypted key-value storage meant for non-sensitive data, so auth tokens should live in secure storage backed by the iOS Keychain and the...
AsyncStorage is unencrypted key-value storage meant for non-sensitive data, so auth tokens should live in secure storage backed by the iOS Keychain and the Android Keystore instead. Secure storage does not make tokens impossible to steal, but it raises the bar considerably. Combine it with short-lived access tokens and refresh token rotation to limit the damage of any leak.
Why Is AsyncStorage a Poor Choice for Tokens?
AsyncStorage persists data in plain, unencrypted storage inside the app's sandbox. On a normal, unmodified device that sandbox is protected by the operating system, but the data is readable if the device is rooted or jailbroken, if a backup is extracted, or if another vulnerability exposes app files.
That is acceptable for preferences, cached UI state, and feature flags. It is not acceptable for credentials that grant access to a user's account. The official React Native documentation describes AsyncStorage as unencrypted, which is the clearest signal about what it should hold.
The risk is practical, not theoretical. Support tools, crash reports, debugging utilities, and device backups can all end up touching stored data. A token stored in plain text has a much larger exposure surface than one held in hardware-backed storage.
What Does Secure Storage Actually Provide?
On iOS, secure storage libraries typically write to the Keychain. On Android, they use the Keystore system, often combined with encrypted preferences. Both platforms can protect keys with hardware-backed security on supported devices, and they let you require device authentication such as biometrics before an item can be read.
- Encryption at rest: values are encrypted using keys managed by the operating system.
- Key isolation: keys are not directly accessible to the app's regular file storage.
- Access control: optional biometric or passcode requirements per item.
- Backup behavior: configurable, so secrets are not restored to other devices unintentionally.
Several maintained libraries wrap these platform features for React Native. Behavior differs between them, and between iOS and Android, so read the documentation of the library you choose, especially for accessibility settings, biometric options, and what happens after an app reinstall.
How Should You Design the Session Around Storage?
Storage is only one layer. A safer design reduces the value of any single token. Use short-lived access tokens, and keep the longer-lived refresh token in secure storage. Rotate refresh tokens on use, so a stolen token becomes invalid after the legitimate client refreshes.
- Keep access tokens in memory where possible, and read from secure storage only to refresh.
- Never log tokens, and scrub them from error reports and analytics events.
- Use the authorization code flow with PKCE for OAuth, in the system browser, rather than embedding credentials in the app.
- Clear stored tokens on logout and on refresh failure, and revoke them on the server.
- Do not embed long-lived secrets, such as API keys that grant broad access, in the app bundle at all.
Remember that anything inside the client can eventually be inspected by a determined attacker. Server-side controls, such as token expiry, revocation, rate limiting, and anomaly detection, matter as much as client storage.
What Are the Common Pitfalls?
A frequent mistake is migrating to secure storage but leaving old copies behind. If tokens were previously in AsyncStorage, delete them during the migration so the insecure copy does not survive.
Another pitfall is assuming secure storage is always synchronous or always available. Reads can fail when the device is locked or when biometric prompts are canceled, so handle errors and fall back to a normal login flow. Test on both platforms and on real devices, because simulators do not always reproduce Keychain and Keystore behavior.
Frequently Asked Questions
Is AsyncStorage encrypted?
No. It stores data unencrypted. Use it for non-sensitive data and rely on Keychain or Keystore backed storage for secrets.
Can secure storage be bypassed?
On compromised devices, a skilled attacker may still extract data. Secure storage reduces risk but is not absolute, so pair it with short token lifetimes and server-side revocation.
Should I store the access token or only the refresh token?
Prefer keeping the access token in memory and persisting only the refresh token securely. That limits what an attacker obtains from storage.
Do I need biometrics for token access?
Not always. It adds protection for sensitive apps such as banking, but it also adds friction. Choose based on the sensitivity of the data.
Summary
Keep tokens out of AsyncStorage, use Keychain and Keystore backed storage, shorten token lifetimes, and rotate refresh tokens. Migrate old data, handle storage errors gracefully, and rely on server-side controls as well. Consult the documentation for your storage library and OAuth provider for version-specific behavior.