secure-storage-patterns
expo-secure-store patterns for sensitive data. Use when storing tokens and credentials.
Best use case
secure-storage-patterns is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
expo-secure-store patterns for sensitive data. Use when storing tokens and credentials.
Teams using secure-storage-patterns should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/secure-storage-patterns/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How secure-storage-patterns Compares
| Feature / Agent | secure-storage-patterns | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
expo-secure-store patterns for sensitive data. Use when storing tokens and credentials.
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Secure Storage Patterns Skill
This skill covers secure data storage for React Native with Expo.
## When to Use
Use this skill when:
- Storing authentication tokens
- Saving sensitive user data
- Managing API keys
- Storing encryption keys
## Core Principle
**SECURE BY DEFAULT** - Always use SecureStore for sensitive data, never AsyncStorage.
## Installation
```bash
npx expo install expo-secure-store
```
## Basic Usage
```typescript
import * as SecureStore from 'expo-secure-store';
// Save value
await SecureStore.setItemAsync('authToken', 'your-token-here');
// Get value
const token = await SecureStore.getItemAsync('authToken');
// Delete value
await SecureStore.deleteItemAsync('authToken');
```
## Authentication Token Storage
```typescript
// lib/secureStorage.ts
import * as SecureStore from 'expo-secure-store';
const TOKEN_KEY = 'authToken';
const REFRESH_TOKEN_KEY = 'refreshToken';
const USER_KEY = 'user';
export const secureStorage = {
// Token management
async saveToken(token: string): Promise<void> {
await SecureStore.setItemAsync(TOKEN_KEY, token);
},
async getToken(): Promise<string | null> {
return SecureStore.getItemAsync(TOKEN_KEY);
},
async deleteToken(): Promise<void> {
await SecureStore.deleteItemAsync(TOKEN_KEY);
},
// Refresh token
async saveRefreshToken(token: string): Promise<void> {
await SecureStore.setItemAsync(REFRESH_TOKEN_KEY, token);
},
async getRefreshToken(): Promise<string | null> {
return SecureStore.getItemAsync(REFRESH_TOKEN_KEY);
},
async deleteRefreshToken(): Promise<void> {
await SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY);
},
// User data
async saveUser(user: User): Promise<void> {
await SecureStore.setItemAsync(USER_KEY, JSON.stringify(user));
},
async getUser(): Promise<User | null> {
const userStr = await SecureStore.getItemAsync(USER_KEY);
return userStr ? JSON.parse(userStr) : null;
},
async deleteUser(): Promise<void> {
await SecureStore.deleteItemAsync(USER_KEY);
},
// Clear all auth data
async clearAuth(): Promise<void> {
await Promise.all([
SecureStore.deleteItemAsync(TOKEN_KEY),
SecureStore.deleteItemAsync(REFRESH_TOKEN_KEY),
SecureStore.deleteItemAsync(USER_KEY),
]);
},
};
```
## Auth Store with Secure Storage
```typescript
import { create } from 'zustand';
import { secureStorage } from '@/lib/secureStorage';
interface User {
id: string;
email: string;
name: string;
}
interface AuthState {
user: User | null;
token: string | null;
isAuthenticated: boolean;
isLoading: boolean;
initialize: () => Promise<void>;
login: (email: string, password: string) => Promise<void>;
logout: () => Promise<void>;
}
export const useAuthStore = create<AuthState>((set) => ({
user: null,
token: null,
isAuthenticated: false,
isLoading: true,
initialize: async () => {
try {
const [token, user] = await Promise.all([
secureStorage.getToken(),
secureStorage.getUser(),
]);
if (token && user) {
set({
token,
user,
isAuthenticated: true,
isLoading: false,
});
} else {
set({ isLoading: false });
}
} catch {
set({ isLoading: false });
}
},
login: async (email, password) => {
set({ isLoading: true });
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
set({ isLoading: false });
throw new Error('Login failed');
}
const { user, token, refreshToken } = await response.json();
await Promise.all([
secureStorage.saveToken(token),
secureStorage.saveRefreshToken(refreshToken),
secureStorage.saveUser(user),
]);
set({
user,
token,
isAuthenticated: true,
isLoading: false,
});
},
logout: async () => {
await secureStorage.clearAuth();
set({
user: null,
token: null,
isAuthenticated: false,
});
},
}));
```
## Initialize Auth on App Start
```typescript
// app/_layout.tsx
import { useEffect } from 'react';
import { useAuthStore } from '@/store/authStore';
export default function RootLayout(): React.ReactElement {
const initialize = useAuthStore((state) => state.initialize);
const isLoading = useAuthStore((state) => state.isLoading);
useEffect(() => {
initialize();
}, [initialize]);
if (isLoading) {
return <LoadingScreen />;
}
return <Stack />;
}
```
## Token Refresh Pattern
```typescript
// lib/api.ts
import axios from 'axios';
import { secureStorage } from './secureStorage';
const api = axios.create({
baseURL: process.env.EXPO_PUBLIC_API_URL,
});
// Request interceptor - add token
api.interceptors.request.use(async (config) => {
const token = await secureStorage.getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor - refresh token on 401
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = await secureStorage.getRefreshToken();
if (!refreshToken) {
throw new Error('No refresh token');
}
const response = await axios.post('/api/auth/refresh', {
refreshToken,
});
const { token: newToken } = response.data;
await secureStorage.saveToken(newToken);
originalRequest.headers.Authorization = `Bearer ${newToken}`;
return api(originalRequest);
} catch {
await secureStorage.clearAuth();
// Navigate to login
return Promise.reject(error);
}
}
return Promise.reject(error);
}
);
export default api;
```
## Biometric Authentication
```typescript
import * as LocalAuthentication from 'expo-local-authentication';
export const biometricAuth = {
async isAvailable(): Promise<boolean> {
const compatible = await LocalAuthentication.hasHardwareAsync();
const enrolled = await LocalAuthentication.isEnrolledAsync();
return compatible && enrolled;
},
async authenticate(): Promise<boolean> {
const result = await LocalAuthentication.authenticateAsync({
promptMessage: 'Authenticate to continue',
fallbackLabel: 'Use passcode',
});
return result.success;
},
};
// Usage with secure storage
async function getSecureData(): Promise<string | null> {
const authenticated = await biometricAuth.authenticate();
if (!authenticated) {
throw new Error('Authentication failed');
}
return secureStorage.getToken();
}
```
## Storage Options
```typescript
// SecureStore options
await SecureStore.setItemAsync('key', 'value', {
keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK,
});
// Available options:
// AFTER_FIRST_UNLOCK - accessible after device unlocked once
// AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY - same, but not synced
// ALWAYS - always accessible (less secure)
// ALWAYS_THIS_DEVICE_ONLY - always accessible, not synced
// WHEN_UNLOCKED - only when device is unlocked (default)
// WHEN_UNLOCKED_THIS_DEVICE_ONLY - same, but not synced
```
## When to Use What
```typescript
// ✅ SecureStore - Sensitive data
await SecureStore.setItemAsync('authToken', token);
await SecureStore.setItemAsync('apiKey', key);
await SecureStore.setItemAsync('encryptionKey', key);
// ❌ AsyncStorage - Non-sensitive data only
await AsyncStorage.setItem('theme', 'dark');
await AsyncStorage.setItem('onboardingComplete', 'true');
await AsyncStorage.setItem('lastViewedProduct', productId);
```
## Notes
- Always use SecureStore for tokens and credentials
- SecureStore has ~2KB limit per item
- Data is encrypted using device keychain/keystore
- Consider biometric authentication for extra security
- Clear sensitive data on logout
- Handle storage errors gracefullyRelated Skills
selective-encrypted-storage-pattern
Security pattern for field-level encryption at rest. Use when encrypting specific sensitive data fields before storage, implementing application-level encryption for databases, or when only certain data elements need encryption at rest. Addresses "Leak data at rest" problem.
security-patterns
Zero-trust security patterns for frontend and backend
rust-async-patterns
Master Rust async programming with Tokio, async traits, error handling, and concurrent patterns. Use when building async Rust applications, implementing concurrent systems, or debugging async code.
reasoning-patterns-v2
Use this skill for rigorous theoretical derivation with supercollider mode (G1-G7 simultaneous), diffusion reasoning, and synthesis engine. Applies enhanced Dokkado Protocol with generator hooks, meta-pattern recognition, and cognitive state awareness. Essential for MONAD-level framework development, cross-domain isomorphism detection, and resonant pattern synthesis. Evolution of reasoning-patterns with full gremlin-brain integration.
react-ui-patterns
Modern React UI patterns for loading states, error handling, and data fetching. Use when building UI components, handling async data, or managing UI states.
react-patterns
Modern React patterns and principles. Hooks, composition, performance, TypeScript best practices.
python-testing-patterns
Implement comprehensive testing strategies with pytest, fixtures, mocking, and test-driven development. Use when writing Python tests, setting up test suites, or implementing testing best practices.
python-patterns
Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying.
prompt-engineering-patterns
Master advanced prompt engineering techniques to maximize LLM performance, reliability, and controllability in production. Use when optimizing prompts, improving LLM outputs, or designing production prompt templates.
plugin-patterns
Canvas plugin architecture patterns, best practices, and implementation templates
permission-patterns
Rules for evaluating, classifying, and deduplicating AI tool permissions
patterns/arena-allocator
Arena Allocator Pattern (C-Specific) pattern for C development