secure-storage-patterns

expo-secure-store patterns for sensitive data. Use when storing tokens and credentials.

16 stars

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

$curl -o ~/.claude/skills/secure-storage-patterns/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/secure-storage-patterns/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/secure-storage-patterns/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How secure-storage-patterns Compares

Feature / Agentsecure-storage-patternsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/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 gracefully

Related Skills

selective-encrypted-storage-pattern

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

Zero-trust security patterns for frontend and backend

rust-async-patterns

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

Modern React patterns and principles. Hooks, composition, performance, TypeScript best practices.

python-testing-patterns

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

Python development principles and decision-making. Framework selection, async patterns, type hints, project structure. Teaches thinking, not copying.

prompt-engineering-patterns

16
from diegosouzapw/awesome-omni-skill

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

16
from diegosouzapw/awesome-omni-skill

Canvas plugin architecture patterns, best practices, and implementation templates

permission-patterns

16
from diegosouzapw/awesome-omni-skill

Rules for evaluating, classifying, and deduplicating AI tool permissions

patterns/arena-allocator

16
from diegosouzapw/awesome-omni-skill

Arena Allocator Pattern (C-Specific) pattern for C development