Storing API keys securely on iOS

Updated 2026-08-04

If an app holds a credential on a phone, where it puts it is the whole security story.

The wrong places

AsyncStorage / UserDefaults. Plain, unencrypted. On React Native, AsyncStorage is a file in the app's container. Convenient, and completely unsuitable for a credential.

A plain file. Same problem, more obviously.

In a JS variable, persisted to a log. Credentials leaking into crash reports and analytics payloads is a real and common failure. Never log a key, not even truncated in development — those lines outlive the debugging session.

The right place

The Keychain on iOS, Keystore on Android. Both give you:

In Expo that's expo-secure-store:

import * as SecureStore from "expo-secure-store";

await SecureStore.setItemAsync("stripe_key", key, {
  keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});

const key = await SecureStore.getItemAsync("stripe_key");

The accessibility option matters

WHEN_UNLOCKED_THIS_DEVICE_ONLY is the right default for a revenue credential:

The second half is the important one. Without it, a credential can travel to a restored device — which is convenient and not what you want for a key that reads your revenue.

Face ID as a second gate

For genuinely sensitive values, require biometric authentication before reading:

import * as LocalAuthentication from "expo-local-authentication";

const { success } = await LocalAuthentication.authenticateAsync({
  promptMessage: "Unlock your vault to view API keys",
});
if (!success) return;

FRGMNT gates viewing stored keys behind Face ID, while ordinary data refresh does not prompt — a balance between security theatre and actual friction.

What the Keychain does not protect against

Being honest about the limits:

Which is exactly why the credential itself should be narrow. A read-only restricted key that is extracted is a disclosure; a secret key that is extracted is a loss.

Defence in depth: secure storage, plus a credential that can't do damage.

Why on-device at all

The alternative is a server holding thousands of users' keys — a much larger target, where one breach exposes everyone. Storing on-device means the blast radius of any single compromise is one person.

That trade-off, and its costs, in why FRGMNT has no backend.

Frequently asked

Where should an iOS app store an API key?

In the Keychain, which is encrypted, hardware-backed and excluded from unencrypted backups. AsyncStorage and UserDefaults are unencrypted and unsuitable for credentials.

Is the iOS Keychain safe on a jailbroken device?

Less so. Keychain protections depend on the operating system's integrity, and a jailbroken device weakens them. This is one reason to use read-only credentials that cannot move money even if extracted.

Read next