expo-secure-store & expo-crypto
Segredos, hashing e bytes aleatórios no dispositivo - o cookbook do expo-secure-store e expo-crypto para aplicativos Expo SDK 57 que armazenam tokens de atualização, derivam impressões digitais e geram nonces.
Busque em todas as páginas da documentação
Segredos, hashing e bytes aleatórios no dispositivo - o cookbook do expo-secure-store e expo-crypto para aplicativos Expo SDK 57 que armazenam tokens de atualização, derivam impressões digitais e geram nonces.
Cartão de receita de referência rápida - pronto para copiar e colar.
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);
}Quando usar:
code_verifier PKCE para OAuth - veja auth-session.Quando evitar:
Adaptador de cofre de tokens, purga de logout e chaves de cache com hash e IDs aleatórios 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);
// Também: queryClient.clear(), router.replace('/sign-in') - veja auth-session
}O que isso demonstra:
WHEN_UNLOCKED_THIS_DEVICE_ONLY - exclui a sincronização do iCloud Keychain.| Restrição | Detalhe |
|---|---|
| Tamanho do item | ~2048 bytes - armazene apenas tokens compactos |
keychainAccessible | WHEN_UNLOCKED_THIS_DEVICE_ONLY recomendado para tokens de atualização |
| Web | Limitado - use padrões de armazenamento de autenticação específicos da web |
| Expo Go | Funciona para desenvolvimento - teste o Keychain no cliente de desenvolvimento antes do lançamento |
await SecureStore.setItemAsync("pin.step_up", "verified", {
requireAuthentication: true, // iOS: Face ID para ler
authenticationPrompt: "Confirme a identidade para ver a conta",
});requireAuthentication é um portão biométrico apenas para iOS - combine com ../auth-session/biometrics-with-localauthentication/biometrics-with-localauthentication.md.| API | Propósito |
|---|---|
digestStringAsync | Digests hexadecimais SHA-1/256/512 |
getRandomBytes | Bytes CSPRNG para nonces |
getRandomBytesAsync | Variante assíncrona para buffers grandes |
randomUUID | Strings UUID v4 |
import * as Crypto from "expo-crypto";
const verifier = Crypto.getRandomBytes(32);
const uuid = Crypto.randomUUID();getRandomBytes para verifiers PKCE - nunca Math.random().Token de atualização → expo-secure-store
Token de acesso → memória ou sessão SecureStore curta
Preferências do usuário → AsyncStorage / MMKV
Fotos → caminhos do expo-file-system// Sequência mínima de logout
await SecureStore.deleteItemAsync("auth.refresh_token");
queryClient.clear();
router.replace("/sign-in");userId - auth.refresh_token.${userId}.Math.random para verifier OAuth - Previsível. Correção: Crypto.getRandomBytes.deleteItemAsync.| Alternativa | Use Quando | Não Use Quando |
|---|---|---|
expo-secure-store | Tokens, pequenos segredos | Documentos JSON de múltiplos KB |
| MMKV Criptografado | KV criptografado grande com chave personalizada | Tokens de atualização sem suporte de hardware |
| Sessões apenas do servidor | Aplicativos de estilo de cookie da web | Necessidades de atualização offline móvel |
Digests expo-crypto | Chaves de cache, impressões digitais | Criptografar arquivos grandes em repouso |
npx expo install expo-secure-store expo-cryptoNenhum plugin de configuração necessário para SecureStore básico no SDK 57.
Versões da Stack: Esta página foi escrita para React 19.2.3, React Native 0.86.0 e Expo SDK 57 (
expo~57.0.4).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026