Skip to content

Commit af336c3

Browse files
authored
chore: add AGENTS.md (#791)
1 parent af7612e commit af336c3

1 file changed

Lines changed: 255 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
# Agents.md — AI Coding Agent Guide for react-native-keychain
2+
3+
## Project Overview
4+
5+
**react-native-keychain** is a React Native library that provides secure credential storage using iOS Keychain and Android Keystore. It supports biometric authentication (Face ID, Touch ID, fingerprint), multiple encryption strategies, and both the Old and New (TurboModule/Fabric) React Native architectures.
6+
7+
- **Package name**: `react-native-keychain`
8+
- **Version**: 10.0.0
9+
- **License**: MIT
10+
- **Repository**: https://github.com/oblador/react-native-keychain
11+
- **Documentation**: https://oblador.github.io/react-native-keychain
12+
- **Platforms**: iOS 9+, Android API 23+, macOS Catalyst, visionOS 1.0+, tvOS 9+
13+
14+
## Repository Structure
15+
16+
| Directory / File | Purpose |
17+
|------------------|---------|
18+
| `src/` | TypeScript source — public API (`index.ts`), enums, types, option normalization, and the TurboModule spec |
19+
| `lib/` | Build output (commonjs, ESM, type declarations) — generated by `yarn prepare`, do not edit |
20+
| `android/` | Android native code (Kotlin). Key areas: `KeychainModule.kt` (RN module), `cipherStorage/` (encryption implementations), `resultHandler/` (biometric prompt flow), `exceptions/` |
21+
| `ios/RNKeychainManager/` | iOS native code (Obj-C++). Single `.h`/`.mm` pair implementing the Keychain bridge |
22+
| `KeychainExample/` | Example/test app. `src/App.tsx` is the demo UI; `e2e/` contains Detox E2E tests and helpers |
23+
| `website/` | Docusaurus documentation site with versioned docs and TypeDoc-generated API reference |
24+
| `.github/workflows/` | GitHub Actions CI/CD (E2E tests, lint, docs deploy, NPM publish) |
25+
| `RNKeychain.podspec` | CocoaPods spec for iOS/macOS/tvOS/visionOS |
26+
| `package.json` | Library manifest, workspace config, and react-native-builder-bob build targets |
27+
28+
## Tech Stack
29+
30+
| Layer | Technology |
31+
|-------|-----------|
32+
| **JS/TS** | TypeScript (strict mode), React Native TurboModules |
33+
| **Android** | Kotlin, Android Keystore, AndroidX Biometric (1.1.0), AndroidX DataStore, Coroutines |
34+
| **iOS** | Objective-C++ (.mm), Security.framework, LocalAuthentication.framework |
35+
| **Build** | react-native-builder-bob (commonjs + ESM + typedefs) |
36+
| **Package Manager** | Yarn 3.6.1 (workspaces) |
37+
| **Tests** | Detox (E2E), Jest |
38+
| **Docs** | Docusaurus 3 + TypeDoc |
39+
| **CI/CD** | GitHub Actions |
40+
| **Codegen** | React Native Codegen (`RNKeychainSpec`) for New Architecture |
41+
42+
## Architecture
43+
44+
### Native Bridge
45+
46+
The library uses React Native's **TurboModule** system (New Architecture) with backward compatibility for the Bridge (Old Architecture):
47+
48+
1. `src/NativeKeychainManager.ts` defines the `Spec` interface via `TurboModuleRegistry.getEnforcing<Spec>('RNKeychainManager')`.
49+
2. `src/index.ts` wraps all native calls with TypeScript types and option normalization.
50+
3. On iOS, `RNKeychainManager.mm` conditionally implements `NativeKeychainManagerSpec` (New Arch) or `RCTBridgeModule` (Old Arch).
51+
4. On Android, `KeychainModule.kt` is a `@ReactModule` supporting both architectures via conditional source sets (`src/newarch` vs `src/oldarch`).
52+
53+
### Android Encryption Strategy
54+
55+
Android uses a pluggable cipher storage system:
56+
57+
- **AES-GCM** (`CipherStorageKeystoreAesGcm`): Primary cipher with optional biometric auth
58+
- **AES-GCM No Auth** (`CipherStorageKeystoreAesGcm` without auth): For non-biometric storage
59+
- **RSA-ECB** (`CipherStorageKeystoreRsaEcb`): Asymmetric encryption with biometric auth
60+
- **AES-CBC** (`CipherStorageKeystoreAesCbc`): Legacy/deprecated
61+
62+
Result handlers manage the biometric prompt flow: `ResultHandlerInteractiveBiometric` for interactive auth, `ResultHandlerNonInteractive` for no-auth operations.
63+
64+
### iOS Keychain Integration
65+
66+
iOS uses Security.framework directly:
67+
- `kSecClassGenericPassword` for generic passwords
68+
- `kSecClassInternetPassword` for internet credentials
69+
- Supports all `kSecAttrAccessible*` levels and `SecAccessControl` for biometric gates
70+
- Safari shared web credentials via `SecRequestSharedWebCredential` / `SecAddSharedWebCredential`
71+
72+
## Public API
73+
74+
All functions are exported from `src/index.ts`:
75+
76+
| Function | Description |
77+
|----------|-------------|
78+
| `setGenericPassword(username, password, options?)` | Store credentials by service |
79+
| `getGenericPassword(options?)` | Retrieve credentials by service |
80+
| `hasGenericPassword(options?)` | Check if credentials exist |
81+
| `resetGenericPassword(options?)` | Delete credentials by service |
82+
| `getAllGenericPasswordServices(options?)` | List all service keys |
83+
| `setInternetCredentials(server, username, password, options?)` | Store credentials by server URL |
84+
| `getInternetCredentials(server, options?)` | Retrieve credentials by server |
85+
| `hasInternetCredentials(options)` | Check if internet credentials exist |
86+
| `resetInternetCredentials(options)` | Delete internet credentials |
87+
| `getSupportedBiometryType()` | Get device biometry type |
88+
| `canImplyAuthentication(options?)` | Check auth policy support (iOS) |
89+
| `getSecurityLevel(options?)` | Get security level (Android) |
90+
| `isPasscodeAuthAvailable()` | Check passcode auth availability |
91+
| `requestSharedWebCredentials()` | Get Safari shared credentials (iOS) |
92+
| `setSharedWebCredentials(server, username, password?)` | Set Safari shared credentials (iOS) |
93+
94+
### Key Enums
95+
96+
- **`ACCESSIBLE`**: When keychain items are accessible (e.g., `WHEN_UNLOCKED`, `AFTER_FIRST_UNLOCK`)
97+
- **`ACCESS_CONTROL`**: Auth gates (e.g., `BIOMETRY_ANY`, `DEVICE_PASSCODE`, `BIOMETRY_ANY_OR_DEVICE_PASSCODE`)
98+
- **`AUTHENTICATION_TYPE`**: Biometrics vs passcode authentication
99+
- **`SECURITY_LEVEL`**: Android security level (`ANY`, `SECURE_SOFTWARE`, `SECURE_HARDWARE`)
100+
- **`STORAGE_TYPE`**: Android cipher type (`AES_GCM`, `AES_GCM_NO_AUTH`, `RSA`, `AES_CBC`)
101+
- **`BIOMETRY_TYPE`**: Device biometry (`TOUCH_ID`, `FACE_ID`, `OPTIC_ID`, `FINGERPRINT`, `FACE`, `IRIS`)
102+
- **`ERROR_CODE`**: Standardized error codes (`PASSCODE_NOT_SET`, `BIOMETRIC_NOT_ENROLLED`, etc.)
103+
104+
### Key Types
105+
106+
- **`SetOptions`**: `accessible`, `securityLevel`, `storage`, `accessControl`, `authenticationPrompt`, `service`, `cloudSync`, `accessGroup`
107+
- **`GetOptions`**: `accessControl`, `authenticationPrompt`, `service`
108+
- **`BaseOptions`**: `service`, `server`, `cloudSync`, `accessGroup`
109+
- **`Result`**: `{ service, storage }`
110+
- **`UserCredentials`**: `{ username, password, service, storage }`
111+
- **`AuthenticationPrompt`**: `{ title?, subtitle?, description?, cancel? }`
112+
113+
## Development
114+
115+
### Prerequisites
116+
117+
- Node.js >= 16
118+
- Yarn 3.6.1 (uses workspaces)
119+
- Xcode (for iOS)
120+
- Android SDK with API 23+ (for Android)
121+
- CocoaPods (for iOS)
122+
123+
### Setup
124+
125+
```bash
126+
# Install dependencies (all workspaces)
127+
yarn install
128+
129+
# Build the library (TypeScript → lib/)
130+
yarn prepare
131+
132+
# Run typecheck
133+
yarn typecheck
134+
135+
# Run linter
136+
yarn lint
137+
138+
# Format code
139+
yarn format
140+
```
141+
142+
### Example App
143+
144+
```bash
145+
cd KeychainExample
146+
147+
# iOS
148+
pod install --project-directory=ios
149+
yarn start # Metro bundler
150+
# Build & run via Xcode or react-native run-ios
151+
152+
# Android
153+
yarn start # Metro bundler
154+
# Build & run via Android Studio or react-native run-android
155+
```
156+
157+
### Running E2E Tests
158+
159+
Tests use **Detox** with physical emulator/simulator:
160+
161+
```bash
162+
cd KeychainExample
163+
164+
# iOS
165+
yarn test:ios # Builds + runs Detox tests
166+
167+
# Android
168+
yarn test:android # Builds + runs Detox tests
169+
```
170+
171+
Test specs are in `KeychainExample/e2e/testCases/`. They cover:
172+
- Access control (passcode, biometric) for both credential types
173+
- Storage types (AES-CBC, AES-GCM, AES-GCM-NO-AUTH, RSA) — Android only
174+
- Security levels (Any, Software, Hardware) — Android only
175+
176+
E2E tests use biometric simulation: `device.matchFace()` on iOS, ADB fingerprint touch on Android. Passcode on Android is `1111`.
177+
178+
### Build Output
179+
180+
`yarn prepare` (via react-native-builder-bob) generates:
181+
- `lib/commonjs/` — CommonJS modules (with ESM interop)
182+
- `lib/module/` — ES modules
183+
- `lib/typescript/` — Type declarations
184+
185+
## CI/CD Pipelines
186+
187+
| Workflow | Trigger | What it does |
188+
|----------|---------|-------------|
189+
| `e2e_tests.yaml` | Push/PR to master | Builds APKs + iOS app, runs Detox E2E tests on Android API 33/34/35 emulators and iOS simulator |
190+
| `lint_and_types.yaml` | Push/PR to master | Runs ESLint + TypeScript typecheck |
191+
| `docs.yaml` | Release created | Builds Docusaurus site, deploys to GitHub Pages |
192+
| `deploy.yaml` | Release created | Builds library via bob, publishes to NPM |
193+
194+
## Coding Conventions
195+
196+
- **TypeScript**: Strict mode, no unused locals/parameters, `verbatimModuleSyntax`, ESNext target
197+
- **Android**: Kotlin with coroutines, Java 17 compatibility, AndroidX libraries
198+
- **iOS**: Objective-C++ (.mm files), supports both TurboModule and Bridge
199+
- **Formatting**: Prettier for JS/TS/JSON/MD files
200+
- **Linting**: ESLint with `@react-native/eslint-config`
201+
- **Option normalization**: All public functions normalize options through `normalizeAuthPrompt()` which sets default `title` and `cancel` for auth prompts
202+
203+
## Error Handling
204+
205+
Both platforms use standardized error codes (defined in `ERROR_CODE` enum):
206+
207+
| Code | Meaning |
208+
|------|---------|
209+
| `E_PASSCODE_NOT_SET` | Device has no passcode/PIN configured |
210+
| `E_BIOMETRIC_NOT_ENROLLED` | No biometrics enrolled on device |
211+
| `E_BIOMETRIC_UNAVAILABLE` | Biometric hardware unavailable |
212+
| `E_BIOMETRIC_LOCKOUT` | Too many failed biometric attempts |
213+
| `E_AUTH_CANCELED` | User canceled authentication |
214+
| `E_AUTH_ERROR` | General authentication error |
215+
| `E_INVALID_PARAMETERS` | Invalid parameters passed |
216+
| `E_STORAGE_ACCESS_ERROR` | Keychain/Keystore access error |
217+
| `E_INTERNAL_ERROR` | Unexpected internal error |
218+
219+
## Common Tasks for Agents
220+
221+
### Adding a new public API method
222+
223+
1. Add the method signature to `src/NativeKeychainManager.ts` (`Spec` interface)
224+
2. Implement the TypeScript wrapper in `src/index.ts` with JSDoc
225+
3. Add relevant types to `src/types.ts` if needed
226+
4. Implement the native method in `ios/RNKeychainManager/RNKeychainManager.mm`
227+
5. Implement the native method in `android/src/main/java/com/oblador/keychain/KeychainModule.kt`
228+
6. Add to the default export object in `src/index.ts`
229+
7. Run `yarn typecheck` to validate TypeScript
230+
8. Add E2E test coverage in `KeychainExample/e2e/testCases/`
231+
232+
### Adding a new enum value
233+
234+
1. Add the value to the relevant enum in `src/enums.ts`
235+
2. Handle the new value in the corresponding native code (iOS `.mm` and/or Android `.kt`)
236+
3. Update the example app (`KeychainExample/src/App.tsx`) if it has a selector for that enum
237+
238+
### Adding a new Android cipher storage
239+
240+
1. Create a new class in `android/src/main/java/com/oblador/keychain/cipherStorage/` implementing `CipherStorage`
241+
2. Register it in `KeychainModule.kt`
242+
3. Add a corresponding `STORAGE_TYPE` enum value in `src/enums.ts`
243+
4. Add E2E test in `storageTypesTest.spec.js`
244+
245+
### Modifying iOS Keychain behavior
246+
247+
1. Edit `ios/RNKeychainManager/RNKeychainManager.mm`
248+
2. Map any new error domains to standardized `ERROR_CODE` values
249+
3. Guard platform-specific code with `TARGET_OS_IOS`, `TARGET_OS_VISION`, or `TARGET_OS_UIKITFORMAC`
250+
251+
### Updating documentation
252+
253+
1. Edit Markdown files in `website/docs/`
254+
2. API docs are auto-generated from TSDoc comments in `src/index.ts` via TypeDoc
255+
3. For new versions, use Docusaurus versioning (existing versions: 9.0.x, 9.1.x, 9.2.x)

0 commit comments

Comments
 (0)