agents-inc/skills/src/skills/mobile-security-react-native/SKILL.md
mobile-security-react-native
Secure storage, certificate pinning, biometric auth, jailbreak detection, code obfuscation, network security, screenshot prevention for React Native
- Source repository stars
- 23
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-09
- Source checked
- 2026-08-28
Decision brief
What it does: where it fits
Quick Guide: Defense-in-depth: layer secure storage (expo-secure-store or react-native-keychain), certificate pinning, biometric authentication, jailbreak/root detection, and code obfuscation. Never store secrets in AsyncStorage or JS bundles. Use Hermes bytecode as your first o…
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-security-react-native"Inspect the Agent Skill "mobile-security-react-native" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/mobile-security-react-native/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)
Storing credentials, tokens, or sensitive data on deviceImplementing certificate pinning to prevent MITM attacksAdding biometric authentication (Face ID, Touch ID, fingerprint) - 02
Philosophy
Mobile security is defense-in-depth -- no single measure is sufficient. Attackers can bypass any individual protection, so layer multiple defenses: secure storage protects data at rest, certificate pinning protects data in transit, biometric authentication protects access, jailb…
Never trust the client -- all sensitive operations need server-side validation. Client-side checks are speed bumps, not walls.Hardware-backed storage -- iOS Keychain and Android Keystore provide hardware-level encryption. AsyncStorage is a plain-text file.HTTPS everywhere -- enforce TLS for all network communication. Certificate pinning adds a second layer against compromised CAs. - 03
Core Patterns
Two main libraries: expo-secure-store (Expo-managed, simpler API, 2KB value limit) and react-native-keychain (bare RN, biometric-protected credentials, no size limit).
Two main libraries: expo-secure-store (Expo-managed, simpler API, 2KB value limit) and react-native-keychain (bare RN, biometric-protected credentials, no size limit).expo-secure-store uses iOS Keychain and Android Keystore-encrypted SharedPreferences. Values are strings with a 2KB limit. Supports biometric gating via requireAuthentication.react-native-keychain provides credential storage with granular access control and biometric gating via ACCESSCONTROL options. - 04
Pattern 1: Secure Storage
Two main libraries: expo-secure-store (Expo-managed, simpler API, 2KB value limit) and react-native-keychain (bare RN, biometric-protected credentials, no size limit).
Two main libraries: expo-secure-store (Expo-managed, simpler API, 2KB value limit) and react-native-keychain (bare RN, biometric-protected credentials, no size limit).expo-secure-store uses iOS Keychain and Android Keystore-encrypted SharedPreferences. Values are strings with a 2KB limit. Supports biometric gating via requireAuthentication.react-native-keychain provides credential storage with granular access control and biometric gating via ACCESSCONTROL options. - 05
Pattern 2: Certificate Pinning
Pin your server's public key hashes to prevent MITM attacks even when a device's CA store is compromised. Use react-native-ssl-public-key-pinning for a JS-level approach that works with all HTTP clients.
Pin your server's public key hashes to prevent MITM attacks even when a device's CA store is compromised. Use react-native-ssl-public-key-pinning for a JS-level approach that works with all HTTP clients.Why good: intercepts all fetch/XMLHttpRequest calls globally, no native code changes required, expiration date prevents bricking when certificates rotateGotcha: iOS requires at least two hashes per domain. Providing only one causes initializeSslPinning to throw.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 92/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 23 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- agents-inc/skills
- Skill path
- src/skills/mobile-security-react-native/SKILL.md
- Commit
- 81d43a51211aca12c85dcc16085fa99014ec548e
- License
- MIT
- Collected
- 2026-08-28
- Default branch
- main
View the original SKILL.md
React Native Security Patterns
Quick Guide: Defense-in-depth: layer secure storage (expo-secure-store or react-native-keychain), certificate pinning, biometric authentication, jailbreak/root detection, and code obfuscation. Never store secrets in AsyncStorage or JS bundles. Use Hermes bytecode as your first obfuscation layer. iOS Keychain persists across reinstalls; Android Keystore does not. Certificate pins require at least two hashes (primary + backup) on iOS.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST NEVER store tokens, passwords, API keys, or PII in AsyncStorage or plain-text files -- use hardware-backed secure storage)
(You MUST use at least two public key hashes for certificate pinning on iOS -- TrustKit/iOS enforces this and will throw if only one is provided)
(You MUST treat jailbreak/root detection as one layer in defense-in-depth -- client-side checks can be bypassed, always validate server-side too)
(You MUST configure both iOS ATS and Android Network Security Config to enforce HTTPS -- never ship with NSAllowArbitraryLoads: true in production)
(You MUST add NSFaceIDUsageDescription to Info.plist when using Face ID -- the OS silently falls back to passcode without it)
</critical_requirements>
Auto-detection: secure storage, SecureStore, expo-secure-store, react-native-keychain, Keychain, Keystore, certificate pinning, SSL pinning, react-native-ssl-public-key-pinning, TrustKit, jailbreak detection, root detection, jail-monkey, biometric authentication, expo-local-authentication, Face ID, Touch ID, fingerprint, code obfuscation, Hermes bytecode, ProGuard, R8, screen capture prevention, App Transport Security, Network Security Config, MITM
When to use:
- Storing credentials, tokens, or sensitive data on device
- Implementing certificate pinning to prevent MITM attacks
- Adding biometric authentication (Face ID, Touch ID, fingerprint)
- Detecting jailbroken/rooted devices
- Hardening builds with code obfuscation (Hermes, ProGuard/R8)
- Preventing screenshot/screen recording of sensitive screens
- Configuring network security (ATS on iOS, Network Security Config on Android)
When NOT to use:
- General React Native component architecture (not a security concern)
- Server-side API security (use your backend security approach)
- Web-only applications (web security patterns differ fundamentally)
Key patterns covered:
- Secure storage with expo-secure-store and react-native-keychain
- Certificate pinning with react-native-ssl-public-key-pinning
- Biometric authentication with expo-local-authentication and react-native-keychain
- Jailbreak/root detection with jail-monkey
- Code obfuscation: Hermes bytecode, Metro transformer, ProGuard/R8
- Network security: iOS ATS and Android Network Security Config
- Screenshot and screen recording prevention
- Defense-in-depth strategy and security layering
Detailed Resources:
- examples/core.md - Secure storage, certificate pinning, biometric auth
- examples/hardening.md - Code obfuscation, jailbreak detection, screenshot prevention, network config
- reference.md - Security checklist, library API reference, pin hash commands
Philosophy
Mobile security is defense-in-depth -- no single measure is sufficient. Attackers can bypass any individual protection, so layer multiple defenses: secure storage protects data at rest, certificate pinning protects data in transit, biometric authentication protects access, jailbreak detection identifies compromised environments, and code obfuscation raises the cost of reverse engineering.
Core principles:
- Never trust the client -- all sensitive operations need server-side validation. Client-side checks are speed bumps, not walls.
- Hardware-backed storage -- iOS Keychain and Android Keystore provide hardware-level encryption. AsyncStorage is a plain-text file.
- HTTPS everywhere -- enforce TLS for all network communication. Certificate pinning adds a second layer against compromised CAs.
- Minimal data exposure -- store the least sensitive data possible on device. Prefer short-lived tokens over long-lived credentials.
- Fail secure -- when security checks fail (biometric, jailbreak), deny access by default rather than falling back to insecure paths.
Mental model:
Think of mobile security as concentric rings. Each ring (secure storage, pinning, biometrics, obfuscation, jailbreak detection) independently slows attackers. The combination creates a security posture that makes exploitation impractical for most threat models.
Platform differences that matter:
| Concern | iOS | Android |
|---|---|---|
| Secure storage | Keychain (persists across reinstalls) | Keystore + SharedPreferences (cleared on uninstall) |
| Biometrics | Face ID / Touch ID | Fingerprint / Face Unlock (weak vs strong) |
| Network security | ATS (default HTTPS since iOS 9) | Network Security Config (clear text blocked API 28+) |
| Code protection | Hermes bytecode (no ProGuard for JS) | Hermes bytecode + ProGuard/R8 for native/Java |
| Screenshot prevention | Effective (screen recording + screenshots) | FLAG_SECURE (effective for screenshots, partial for recording) |
Core Patterns
Pattern 1: Secure Storage
Two main libraries: expo-secure-store (Expo-managed, simpler API, 2KB value limit) and react-native-keychain (bare RN, biometric-protected credentials, no size limit).
expo-secure-store uses iOS Keychain and Android Keystore-encrypted SharedPreferences. Values are strings with a ~2KB limit. Supports biometric gating via requireAuthentication.
import * as SecureStore from "expo-secure-store";
const AUTH_TOKEN_KEY = "auth-token";
// Store securely with biometric protection
await SecureStore.setItemAsync(AUTH_TOKEN_KEY, token, {
requireAuthentication: true,
authenticationPrompt: "Authenticate to save credentials",
});
// Retrieve (prompts biometric if requireAuthentication was set)
const stored = await SecureStore.getItemAsync(AUTH_TOKEN_KEY);
react-native-keychain provides credential storage with granular access control and biometric gating via ACCESS_CONTROL options.
import * as Keychain from "react-native-keychain";
await Keychain.setGenericPassword("[email protected]", token, {
accessControl: Keychain.ACCESS_CONTROL.BIOMETRY_ANY_OR_DEVICE_PASSCODE,
accessible: Keychain.ACCESSIBLE.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
});
Why good: hardware-backed encryption, biometric gating prevents unauthorized reads, WHEN_UNLOCKED_THIS_DEVICE_ONLY prevents extraction from backups
See examples/core.md for full secure storage patterns with error handling and library comparison.
Pattern 2: Certificate Pinning
Pin your server's public key hashes to prevent MITM attacks even when a device's CA store is compromised. Use react-native-ssl-public-key-pinning for a JS-level approach that works with all HTTP clients.
import { initializeSslPinning } from "react-native-ssl-public-key-pinning";
const PIN_EXPIRATION_DATE = "2026-12-31";
await initializeSslPinning({
"api.example.com": {
includeSubdomains: true,
publicKeyHashes: [
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", // Primary
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=", // Backup (REQUIRED on iOS)
],
expirationDate: PIN_EXPIRATION_DATE,
},
});
Why good: intercepts all fetch/XMLHttpRequest calls globally, no native code changes required, expiration date prevents bricking when certificates rotate
Gotcha: iOS requires at least two hashes per domain. Providing only one causes initializeSslPinning to throw.
See examples/core.md for rotation strategy and reference.md for pin hash generation commands.
Pattern 3: Biometric Authentication
Two approaches: expo-local-authentication (standalone biometric prompt, no credential storage) and react-native-keychain (biometric-gated credential retrieval).
import * as LocalAuthentication from "expo-local-authentication";
async function authenticateUser(): Promise<boolean> {
const hasHardware = await LocalAuthentication.hasHardwareAsync();
const isEnrolled = await LocalAuthentication.isEnrolledAsync();
if (!hasHardware || !isEnrolled) return false;
const result = await LocalAuthentication.authenticateAsync({
promptMessage: "Verify your identity",
disableDeviceFallback: false,
cancelLabel: "Cancel",
});
return result.success;
}
Why good: checks hardware + enrollment before prompting, disableDeviceFallback: false allows passcode as backup, clean boolean result
Gotcha: Face ID requires NSFaceIDUsageDescription in Info.plist. Without it, iOS silently falls back to passcode (no error, no Face ID prompt). Face ID is not supported in Expo Go -- use a development build.
See examples/core.md for biometric-gated credential flow combining both libraries.
Pattern 4: Jailbreak/Root Detection
Detect compromised devices where security controls (sandboxing, code signing) are disabled. Use jail-monkey for detection checks.
import JailMonkey from "jail-monkey";
function getDeviceSecurityStatus() {
return {
isJailbroken: JailMonkey.isJailBroken(),
canMockLocation: JailMonkey.canMockLocation(),
isDebugMode: JailMonkey.isDebuggedMode(),
isOnExternalStorage: JailMonkey.isOnExternalStorage(), // Android only
};
}
Why good: checks multiple indicators (not just one file path), includes location mocking and debug detection
Important: Jailbreak detection is a speed bump, not a wall. Determined attackers bypass client-side checks with hooking frameworks (Frida, Objection). Always pair with server-side device attestation for high-security apps.
See examples/hardening.md for response strategies and server-side validation.
Pattern 5: Code Obfuscation
Layer 1: Hermes bytecode (enabled by default since RN 0.70) compiles JS to bytecode, making casual reverse engineering difficult. Layer 2: Metro obfuscation transformer for additional string encryption and control flow flattening. Layer 3: ProGuard/R8 for Android native/Java code.
// metro.config.js -- adding obfuscation transformer
const { getDefaultConfig } = require("@react-native/metro-config");
const config = getDefaultConfig(__dirname);
config.transformer = {
...config.transformer,
babelTransformerPath: require.resolve("react-native-obfuscating-transformer"),
};
module.exports = config;
Why good: layered approach -- Hermes handles baseline, transformer adds string/flow obfuscation, ProGuard/R8 covers native code
Gotcha: Not all obfuscation options work. The stringArray option in react-native-obfuscating-transformer is known to break builds. Test thoroughly.
See examples/hardening.md for ProGuard/R8 configuration and obfuscation options.
Pattern 6: Network Security Configuration
Enforce HTTPS at the OS level. iOS uses App Transport Security (ATS), Android uses Network Security Config.
iOS (Info.plist): ATS enforces HTTPS by default since iOS 9. Never ship with NSAllowArbitraryLoads: true.
Android (network_security_config.xml): Clear text blocked by default since API 28. Pin certificates natively for defense-in-depth alongside JS-level pinning.
<!-- android/app/src/main/res/xml/network_security_config.xml -->
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-12-31">
<pin digest="SHA-256">AAAAAAAAAA...=</pin>
<pin digest="SHA-256">BBBBBBBBBB...=</pin>
</pin-set>
</domain-config>
</network-security-config>
Why good: OS-level enforcement, clear text blocked globally, native pinning adds second layer beyond JS-level pinning
See examples/hardening.md for iOS ATS configuration and debug vs release network policies.
Pattern 7: Screenshot and Screen Recording Prevention
Prevent screen capture on sensitive screens (banking, credentials, personal data).
import { useIsFocused } from "@react-navigation/native";
import * as ScreenCapture from "expo-screen-capture";
import { useEffect } from "react";
function usePreventCapture() {
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
ScreenCapture.preventScreenCaptureAsync();
} else {
ScreenCapture.allowScreenCaptureAsync();
}
return () => {
ScreenCapture.allowScreenCaptureAsync();
};
}, [isFocused]);
}
Why good: per-screen control (not global), cleanup on unfocus/unmount, works for both screenshots and screen recording on iOS
Gotcha: On Android, FLAG_SECURE reliably blocks screenshots but screen recording prevention varies by Android version. On iOS, screenshot content is replaced with blank but the user can still trigger the screenshot action.
See examples/hardening.md for non-Expo alternatives and listener-based approaches.
<decision_framework>
Decision Framework
Secure Storage Choice
Need to store credentials/tokens securely?
+-- Using Expo managed workflow?
| +-- YES -> expo-secure-store (simpler API, Expo-native)
| +-- NO -> react-native-keychain (more control, biometric options)
|
+-- Need biometric-gated credential retrieval?
| +-- YES -> react-native-keychain with ACCESS_CONTROL.BIOMETRY_ANY
| +-- OR -> expo-secure-store with requireAuthentication: true
|
+-- Value larger than 2KB?
| +-- YES -> react-native-keychain (no size limit)
| +-- NO -> Either library works
|
+-- Need credential persistence across app reinstalls?
+-- iOS -> Both persist (Keychain behavior)
+-- Android -> Neither persists (cleared on uninstall)
Biometric Authentication Choice
Need biometric prompt (no credential storage)?
+-- YES -> expo-local-authentication
|
Need biometric-gated credential storage/retrieval?
+-- YES -> react-native-keychain with accessControl
|
Need to distinguish biometric security level (weak vs strong)?
+-- YES -> expo-local-authentication (provides SecurityLevel enum)
Certificate Pinning Approach
Need SSL pinning?
+-- JS-level (works with all HTTP clients)?
| +-- YES -> react-native-ssl-public-key-pinning
|
+-- Native-level (defense-in-depth)?
| +-- iOS -> TrustKit (via CocoaPods)
| +-- Android -> Network Security Config XML
|
+-- Best practice -> Both JS-level AND native-level
Security Layering
Minimum viable security:
1. Secure storage (expo-secure-store or react-native-keychain)
2. HTTPS enforcement (ATS + Network Security Config)
3. Hermes bytecode (default since RN 0.70)
Standard security (most apps):
+ Certificate pinning
+ Biometric authentication
+ ProGuard/R8 on Android
High security (banking, healthcare, fintech):
+ Jailbreak/root detection
+ JS code obfuscation transformer
+ Screenshot prevention
+ Server-side device attestation
+ Runtime integrity checks
</decision_framework>
<red_flags>
RED FLAGS
High Priority Issues:
- Storing tokens or credentials in AsyncStorage -- it is a plain-text file, trivially readable on jailbroken/rooted devices
- Storing API keys or secrets in the JS bundle -- the bundle is extractable from any published app
- Shipping with
NSAllowArbitraryLoads: truein production Info.plist -- disables ATS entirely, allows HTTP - Using only one public key hash for certificate pinning on iOS -- TrustKit throws, pinning silently fails
- Relying solely on jailbreak detection for security -- client-side checks are bypassable with Frida/Objection
Medium Priority Issues:
- Not checking
hasHardwareAsyncandisEnrolledAsyncbefore callingauthenticateAsync-- crashes or confusing UX on devices without biometrics - Missing
NSFaceIDUsageDescriptionin Info.plist -- Face ID silently falls back to passcode without any error - Using
ACCESSIBLE.ALWAYSfor Keychain items -- allows access even when device is locked (deprecated on iOS) - Skipping ProGuard/R8 on Android release builds -- native code is trivially decompilable without it
- Not setting
expirationDateon certificate pins -- expired certificates brick the app until users update
Gotchas & Edge Cases:
- iOS Keychain data persists across app reinstalls (same bundle ID); Android Keystore data does not -- plan token refresh accordingly
- expo-secure-store has a ~2KB value size limit -- large tokens or data blobs will silently fail or throw
z.coerce.boolean()treats string"false"as truthy -- when parsing security config from strings, use explicit comparison- Android
FLAG_SECUREblocks screenshots reliably but screen recording prevention varies by OS version and manufacturer - Hermes bytecode can be decompiled with tools like
hbctool-- it raises the bar but is not true encryption - react-native-obfuscating-transformer's
stringArrayoption breaks React Native builds -- avoid it - Android biometrics have "weak" (2D face) vs "strong" (fingerprint, 3D face) security levels -- financial apps should require strong
- Certificate pins must be rotated before expiry -- set calendar reminders and use the
expirationDatefield as a safety net Keychain.ACCESS_CONTROL.BIOMETRY_CURRENT_SETinvalidates credentials when biometrics are re-enrolled -- useBIOMETRY_ANYfor persistence across biometric changes
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST NEVER store tokens, passwords, API keys, or PII in AsyncStorage or plain-text files -- use hardware-backed secure storage)
(You MUST use at least two public key hashes for certificate pinning on iOS -- TrustKit/iOS enforces this and will throw if only one is provided)
(You MUST treat jailbreak/root detection as one layer in defense-in-depth -- client-side checks can be bypassed, always validate server-side too)
(You MUST configure both iOS ATS and Android Network Security Config to enforce HTTPS -- never ship with NSAllowArbitraryLoads: true in production)
(You MUST add NSFaceIDUsageDescription to Info.plist when using Face ID -- the OS silently falls back to passcode without it)
Failure to follow these rules will expose user credentials, enable MITM attacks, and create false security assumptions.
</critical_reminders>
Frequently asked questions
What to verify before installation and use
What does the mobile-security-react-native source document cover?
Quick Guide: Defense-in-depth: layer secure storage (expo-secure-store or react-native-keychain), certificate pinning, biometric authentication, jailbreak/root detection, and code obfuscation. Never store secrets in AsyncStorage or JS bundles. Use Hermes bytecode as your first o…
How do I install mobile-security-react-native?
The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/mobile-security-react-native". Inspect the command and pinned source before running it.
Alternatives
Compare before choosing
event4u-app/agent-config
existing-ui-audit
Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.
UiPath/skills
uipath-coded-apps
UiPath Coded Apps — scaffold, build, run, and deploy Coded Web Apps and Coded Action Apps: React/TypeScript apps that call UiPath Cloud APIs via the `@uipath/uipath-typescript` SDK and ship to Automation Cloud (push/pull to Studio Web, pack, publish, deploy, OAuth-PKCE). Also generates live analytics & governance dashboards from a plain-language request, wired to tenant data via the Insights real-time API, with edit and deploy flows. For RPA→uipath-rpa, Python agents→uipath-agents, Maestro flows
fcakyon/claude-codex-settings
vercel-react-view-transitions
Guide for implementing smooth, native-feeling animations using React's View Transition API (`<ViewTransition>` component, `addTransitionType`, and CSS view transition pseudo-elements). Use this skill whenever the user wants to add page transitions, animate route changes, create shared element animations, animate enter/exit of components, animate list reorder, implement directional (forward/back) navigation animations, or integrate view transitions in Next.js. Also use when the user mentions view
yonatangross/orchestkit
json-render-catalog
json-render component catalog patterns for AI-safe generative UI. Define Zod-typed catalogs that constrain what AI can generate, use @json-render/shadcn for 36 pre-built components, optimize specs with YAML mode, and apply the three edit modes (patch/merge/diff) for progressive updates. Use when building AI-generated UIs, defining component catalogs, or integrating json-render into React/Vue/Svelte/React Native/Ink/Next.js projects.