RORK LABJP
MAX — Rork Max generates native Swift for every Apple platform, from iPhone to Vision ProNATIVE — It reaches native capabilities like AR/LiDAR, Metal 3D, Dynamic Island, Live Activities, and HealthKitPUBLISH — Publish to the App Store in two clicks; Rork Max is $200/monthEXPO — Standard Rork builds iOS and Android together via React Native (Expo) and is free to startPROMPT — Describe your app idea in plain English and Rork generates deployable, store-ready codePRICE — Standard Rork's paid plans start at $25/month: build with it first, then consider Max for native featuresMAX — Rork Max generates native Swift for every Apple platform, from iPhone to Vision ProNATIVE — It reaches native capabilities like AR/LiDAR, Metal 3D, Dynamic Island, Live Activities, and HealthKitPUBLISH — Publish to the App Store in two clicks; Rork Max is $200/monthEXPO — Standard Rork builds iOS and Android together via React Native (Expo) and is free to startPROMPT — Describe your app idea in plain English and Rork generates deployable, store-ready codePRICE — Standard Rork's paid plans start at $25/month: build with it first, then consider Max for native features
Articles/Dev Tools
Dev Tools/2026-06-21Advanced

Where Should Your Rork App Store Auth Tokens? expo-secure-store and a Biometric Gate

How I moved a Rork-generated app's auth tokens out of AsyncStorage into expo-secure-store and put a biometric check in front of every read — including the size limit and sign-out gotchas I hit in production.

Rork431expo-secure-store2authentication4security4biometrics2

Premium Article

When you run a handful of apps for long enough as an indie developer, you eventually notice that the riskiest line of code is usually the first one you ever wrote — and never touched again. For me it was the auth token. The login flow Rork generated wrote both the access and refresh tokens straight into AsyncStorage. It worked, so it sat there for six months.

AsyncStorage is not encrypted. On Android the values live in a plain SQLite store; on iOS they get some protection but not the strength you'd consciously choose for a long-lived secret. If you assume a rooted or jailbroken device, or a leaky backup, plaintext tokens are simply not where credentials belong.

This article walks through moving tokens into expo-secure-store and then putting a biometric check in front of the read — migration and cleanup included. Token refresh and retry are a separate topic; here I'm focused only on storage and retrieval.

You don't have one storage — split by role

The first trap is thinking "just put everything in SecureStore." SecureStore goes through the OS keychain/keystore, so every read and write carries real cost. Push your settings and cache through it and your cold start gets visibly slower.

Split by the nature of the data instead.

DataStoreWhy
Access/refresh tokens, user secretsexpo-secure-storeA leak is immediate harm. Protected by the OS keychain/keystore
Theme, language, onboarding-complete flagsAsyncStorageLow harm if exposed. Cheap to read and write
API response cache, image metadata, bulk dataMMKV / SQLiteHigh volume, needs fast access. Low secrecy

Isolating only the tokens in SecureStore narrows the attack surface considerably while keeping startup overhead minimal.

The basics, and the cost of a read

If you're inside the managed Expo flow, install is one line.

npx expo install expo-secure-store

A thin wrapper pays off later when you start adding options. Keep keys as constants so a typo can't silently split your storage.

// lib/secureToken.ts
import * as SecureStore from "expo-secure-store";
 
const ACCESS_KEY = "auth.accessToken";
const REFRESH_KEY = "auth.refreshToken";
 
// iOS: decryptable only after unlock, never migrates to another device
// Android: AES-encrypted and held in the Keystore
const OPTIONS: SecureStore.SecureStoreOptions = {
  keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
};
 
export async function saveTokens(access: string, refresh: string) {
  await SecureStore.setItemAsync(ACCESS_KEY, access, OPTIONS);
  await SecureStore.setItemAsync(REFRESH_KEY, refresh, OPTIONS);
}
 
export async function getAccessToken(): Promise<string | null> {
  return SecureStore.getItemAsync(ACCESS_KEY, OPTIONS);
}
 
export async function clearTokens() {
  await SecureStore.deleteItemAsync(ACCESS_KEY);
  await SecureStore.deleteItemAsync(REFRESH_KEY);
}

The default for keychainAccessible is WHEN_UNLOCKED, but I usually reach for the *_THIS_DEVICE_ONLY variants. It's a deliberate statement that the token must not travel to another device through iCloud Keychain or a backup. If you genuinely need to refresh tokens in the background, loosen it to AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY — but decide that from the requirement, not by default.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A decision rule for splitting tokens, settings, and cache across SecureStore, AsyncStorage, and MMKV
Putting biometric auth in front of the token read, with a fallback path for lockouts
A run-once migration off AsyncStorage and a sign-out routine that actually clears the device
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Dev Tools2026-04-02
Complete Security Implementation Guide for Rork Apps — Biometrics, Encryption, Secure Storage & Certificate Pinning
Harden your Rork app for production: biometric auth, AES encryption, secure storage, certificate pinning, jailbreak detection, and ATT — with working code.
Dev Tools2026-05-09
5 Things to Check When Google Sign-In Throws redirect_uri_mismatch in Your Rork App
When your Rork app keeps throwing redirect_uri_mismatch on Google Sign-In, the gap is almost always between Google Cloud Console and your Expo config. Here are 5 checks that resolve it for good.
Dev Tools2026-03-25
Rork × Supabase Auth & Realtime Guide — Building a Backend Without Code
Learn how to combine Rork with Supabase to implement authentication and real-time data sync. A practical step-by-step guide from user registration to real-time chat functionality.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →