expo-secure-store & expo-crypto
Secretos, hashing y bytes aleatorios en el dispositivo - el manual de expo-secure-store y expo-crypto para aplicaciones Expo SDK 57 que guardan tokens de actualización, generan huellas digitales y producen nonces.
Busca en todas las páginas de la documentación
Secretos, hashing y bytes aleatorios en el dispositivo - el manual de expo-secure-store y expo-crypto para aplicaciones Expo SDK 57 que guardan tokens de actualización, generan huellas digitales y producen nonces.
Tarjeta de referencia rápida - lista para copiar y pegar.
npx expo install expo-secure-store expo-cryptoimport * as SecureStore from "expo-secure-store";
const REFRESH_KEY = "auth.refresh_token";
export async function saveRefreshToken(token: string) {
await SecureStore.setItemAsync(REFRESH_KEY, token, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
}
export async function loadRefreshToken(): Promise<string | null> {
return SecureStore.getItemAsync(REFRESH_KEY);
}
export async function clearAuthSecrets() {
await SecureStore.deleteItemAsync(REFRESH_KEY);
}import * as Crypto from "expo-crypto";
export async function sha256Hex(input: string) {
return Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256,
input
);
}
export function randomNonce(bytes = 16) {
return Crypto.getRandomBytes(bytes);
}Cuándo usarlo:
code_verifier PKCE para OAuth - ver auth-session.Cuándo evitarlo:
Adaptador de bóveda de tokens, purga de cierre de sesión y claves de caché con hash con IDs aleatorios seguros.
npx expo install expo-secure-store expo-crypto// src/auth/secureTokenVault.ts
import * as SecureStore from "expo-secure-store";
const keys = {
refresh: (userId: string) => `auth.refresh_token.${userId}`,
device: "auth.device_binding",
} as const;
export const tokenVault = {
async saveRefresh(userId: string, token: string) {
await SecureStore.setItemAsync(keys.refresh(userId), token, {
keychainAccessible: SecureStore.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
},
async loadRefresh(userId: string) {
return SecureStore.getItemAsync(keys.refresh(userId));
},
async clearUser(userId: string) {
await SecureStore.deleteItemAsync(keys.refresh(userId));
},
async clearAllKnownUsers(userIds: string[]) {
await Promise.all(userIds.map((id) => tokenVault.clearUser(id)));
await SecureStore.deleteItemAsync(keys.device);
},
};// src/cache/cacheKey.ts
import * as Crypto from "expo-crypto";
export async function cacheKeyForUrl(url: string) {
const hash = await Crypto.digestStringAsync(
Crypto.CryptoDigestAlgorithm.SHA256,
url
);
return `cache:${hash.slice(0, 16)}`;
}// src/auth/logout.ts
import { tokenVault } from "./secureTokenVault";
export async function logout(userId: string, allUserIds: string[]) {
await tokenVault.clearAllKnownUsers(allUserIds);
// También: queryClient.clear(), router.replace('/sign-in') - ver auth-session
}Lo que demuestra esto:
WHEN_UNLOCKED_THIS_DEVICE_ONLY - excluye la sincronización de iCloud Keychain.| Restricción | Detalle |
|---|---|
| Tamaño del elemento | ~2048 bytes - almacena solo tokens compactos |
keychainAccessible | WHEN_UNLOCKED_THIS_DEVICE_ONLY recomendado para tokens de actualización |
| Web | Limitado - usa patrones de almacenamiento de autenticación específicos de web |
| Expo Go | Funciona para desarrollo - prueba Keychain en cliente de desarrollo antes de la versión |
await SecureStore.setItemAsync("pin.step_up", "verified", {
requireAuthentication: true, // iOS: Face ID para leer
authenticationPrompt: "Confirma tu identidad para ver la cuenta",
});requireAuthentication es una puerta biométrica solo para iOS - empareja con ../auth-session/biometrics-with-localauthentication/biometrics-with-localauthentication.md.| API | Propósito |
|---|---|
digestStringAsync | Resúmenes SHA-1/256/512 hexadecimales |
getRandomBytes | Bytes CSPRNG para nonces |
getRandomBytesAsync | Variante asincrónica para búferes grandes |
randomUUID | Cadenas UUID v4 |
import * as Crypto from "expo-crypto";
const verifier = Crypto.getRandomBytes(32);
const uuid = Crypto.randomUUID();getRandomBytes para verificadores PKCE - nunca Math.random().Token de actualización → expo-secure-store
Token de acceso → memoria o sesión breve de SecureStore
Preferencias del usuario → AsyncStorage / MMKV
Fotos → rutas de expo-file-system// Secuencia mínima de cierre de sesión
await SecureStore.deleteItemAsync("auth.refresh_token");
queryClient.clear();
router.replace("/sign-in");userId - auth.refresh_token.${userId}.Math.random para el verificador OAuth - Predecible. Solución: Crypto.getRandomBytes.deleteItemAsync.| Alternativa | Usarlo cuando | No usarlo cuando |
|---|---|---|
expo-secure-store | Tokens, secretos pequeños | Documentos JSON de varios KB |
| MMKV cifrado | KV cifrado grande con clave personalizada | Tokens de actualización sin respaldo de hardware |
| Sesiones solo de servidor | Aplicaciones de cookies al estilo web | Necesidades de actualización sin conexión móvil |
Resúmenes de expo-crypto | Claves de caché, huellas digitales | Cifrado de archivos grandes en reposo |
npx expo install expo-secure-store expo-cryptoNo se requiere complemento de configuración para SecureStore básico en SDK 57.
Versiones de pila: Esta página fue escrita para React 19.2.3, React Native 0.86.0 y Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última actualización: 19 jul 2026