Internet Computer Protocol (ICP) authentication and agent library for React Native
npm install react-native-icp-auth-kitIdentity management and authentication library for Internet Computer Protocol (ICP) in React Native




Features โข Installation โข Quick Start โข Documentation โข Example
---
A complete authentication and identity management solution for building ICP wallet applications in React Native. This library provides everything you need for secure key management, wallet recovery, and canister interaction.
Perfect for:
- ๐ฆ Cryptocurrency wallets
- ๐ Self-custody applications
- ๐ Decentralized apps (dApps) with direct key management
- ๐ผ ICP-based mobile applications
Not for Internet Identity/NFID delegation flows - see ICP-hub/react-native-icp-auth for WebAuthn-based authentication.
---
โ
Ed25519 Identity Management - Generate and manage cryptographic identities
โ
BIP39 Seed Phrase Recovery - Industry-standard 12/24-word recovery phrases
โ
Secure Storage - iOS Keychain & Android Keystore support
โ
Session Persistence - Automatic wallet restoration on app restart
โ
ICP Agent Integration - Simplified canister query and update calls
โ
TypeScript Support - Full type definitions included
โ
Private Key Export/Import - Flexible backup options
โ
Zero Native Dependencies - Pure JavaScript cryptography
---
``bash`
npm install react-native-icp-auth-kit
`bash`
npm install react-native-get-random-values
npm install @react-native-async-storage/async-storage
For secure storage (iOS Keychain/Android Keystore):
`bash`
npm install expo-secure-store
`bash`
cd ios && pod install && cd ..
---
Add this to your app's entry file (e.g., index.js or App.tsx):
`typescript`
import 'react-native-get-random-values'; // Must be first import
import { createAuthClient, generateIdentityWithSeed } from 'react-native-icp-auth-kit';
`typescript
import { createAuthClient, generateIdentityWithSeed } from 'react-native-icp-auth-kit';
async function createWallet() {
// Create auth client
const authClient = await createAuthClient();
// Generate wallet with 12-word seed phrase
const { identity, seedPhrase } = generateIdentityWithSeed();
// Show seed phrase to user - they MUST save it!
console.log('Save this seed phrase:', seedPhrase);
// Login with the identity
await authClient.login(identity);
// Get principal ID
const principal = authClient.getPrincipalText();
console.log('Your Principal:', principal);
}
`
`typescript
async function restoreSession() {
const authClient = await createAuthClient();
// Try to restore previous session
const hasSession = await authClient.autoLogin();
if (hasSession) {
console.log('Welcome back!', authClient.getPrincipalText());
} else {
console.log('No saved wallet found');
}
}
`
`typescript
import { createAgentManager } from 'react-native-icp-auth-kit';
import { idlFactory } from './declarations/my-canister';
async function callCanister() {
const authClient = await createAuthClient();
await authClient.autoLogin();
// Create agent
const agent = createAgentManager({
identity: authClient.getIdentity(),
network: 'mainnet',
});
// Query call (read-only)
const balance = await agent.query({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
methodName: 'getBalance',
idlFactory,
});
// Update call (modifies state)
const result = await agent.update({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
methodName: 'transfer',
args: [{ to: 'principal-id', amount: 100 }],
idlFactory,
});
}
`
---
- Wallet Recovery Methods
- API Reference
- Auth Client
- Agent Manager
- Identity Utilities
- Secure Storage
- Complete Examples
- Architecture
- Security Best Practices
---
Industry standard used by MetaMask, Trust Wallet, and most cryptocurrency wallets.
`typescript
import { generateIdentityWithSeed, fromSeedPhrase } from 'react-native-icp-auth-kit';
// Create wallet with seed phrase
const { identity, seedPhrase } = generateIdentityWithSeed(128); // 12 words
// Or use 256 for 24 words: generateIdentityWithSeed(256)
// Display to user
console.log('Seed Phrase:', seedPhrase);
// Example: "witch collapse practice feed shame open despair creek road again ice least"
// Later: Recover from seed phrase
const recoveredIdentity = fromSeedPhrase(seedPhrase);
// โ
Same principal as original!
`
User Flow:
1. โ
User creates wallet โ Show 12/24-word seed phrase
2. โ
User writes down seed phrase (paper backup recommended)
3. โ
App stores identity in AsyncStorage
4. โ User deletes app โ All local data lost
5. โ
User reinstalls app โ Enters seed phrase โ Wallet recovered!
Derivation Path: Uses BIP44 standard m/44'/223'/0'/0/{accountIndex} where 223 is the coin type for Internet Computer.
For advanced users who prefer manual key management.
`typescript
import { exportPrivateKey, importPrivateKey } from 'react-native-icp-auth-kit';
// Export private key
const identity = generateIdentity();
const privateKeyHex = exportPrivateKey(identity);
console.log('Private Key:', privateKeyHex);
// Example: "a7f3b8c2d9e4f1a6b3c8d2e9f4a1b6c3..."
// Import private key
const recoveredIdentity = importPrivateKey(privateKeyHex);
`
โ ๏ธ Warning: Private keys are more complex than seed phrases. Users may find them harder to backup safely.
Automatic backup that survives app deletion (iOS only with proper configuration).
`typescript
import {
isSecureStorageAvailable,
saveSecureItem,
getSecureItem,
serializeIdentity,
deserializeIdentity
} from 'react-native-icp-auth-kit';
// Check availability (requires expo-secure-store)
if (isSecureStorageAvailable()) {
// Save to secure storage
const identity = generateIdentity();
const serialized = serializeIdentity(identity);
await saveSecureItem('wallet_identity', serialized);
// Restore from secure storage
const stored = await getSecureItem('wallet_identity');
if (stored) {
const identity = deserializeIdentity(stored);
}
}
`
Platform Support:
- โ
iOS: Keychain survives app deletion if properly configured
- โ ๏ธ Android: Keystore data lost on app uninstall
| Method | User Experience | Security | Survives Uninstall | Requires Dependency |
|--------|----------------|----------|-------------------|---------------------|
| Seed Phrase | Manual backup | โญโญโญโญ | โ
(user has backup) | โ No |
| Private Key | Manual export | โญโญโญโญ | โ
(user has backup) | โ No |
| Secure Storage | Automatic | โญโญโญโญโญ | โ ๏ธ iOS only | โ
expo-secure-store |
๐ก Recommendation: Use seed phrase for wallet apps - it's the industry standard and gives users full control.
---
#### createAuthClient(config?): Promise
Creates an authenticated client instance.
`typescript`
const authClient = await createAuthClient();
Parameters:
- config (optional): Configuration objectstorage
- (optional): Custom storage implementation
Returns: Promise resolving to RNAuthClient instance
---
#### authClient.login(identity): Promise
Login with an Ed25519 identity and persist to storage.
`typescript`
const { identity } = generateIdentityWithSeed();
await authClient.login(identity);
Parameters:
- identity: Ed25519KeyIdentity instance
Returns: Promise resolving to the saved identity
---
#### authClient.autoLogin(): Promise
Attempts to restore session from storage automatically.
`typescript`
const hasSession = await authClient.autoLogin();
if (hasSession) {
console.log('Session restored!');
}
Returns: true if session was restored, false otherwise
---
#### authClient.logout(): Promise
Logout and clear all stored identity data.
`typescript`
await authClient.logout();
---
#### authClient.isAuthenticated(): boolean
Check if user is currently authenticated.
`typescript`
if (authClient.isAuthenticated()) {
console.log('User is logged in');
}
Returns: true if authenticated (not anonymous), false otherwise
---
#### authClient.getIdentity(): Identity
Get the current identity object.
`typescript`
const identity = authClient.getIdentity();
---
#### authClient.getPrincipal(): Principal
Get the current principal.
`typescript`
const principal = authClient.getPrincipal();
console.log(principal.toText());
---
#### authClient.getPrincipalText(): string
Get principal as text string.
`typescript`
const principalText = authClient.getPrincipalText();
// Example: "2vxsx-fae" (anonymous) or "hpikg-6exdt-jn33w-ndty3-fc7jc-tl2lr-buih3-cs3y7-tftkp-sfp62-gqe"
---
#### createAgentManager(config): AgentManager
Creates an agent manager for canister interactions.
`typescript`
const agent = createAgentManager({
identity: authClient.getIdentity(),
network: 'mainnet', // or 'local'
fetchRootKey: false, // true ONLY for local development
});
Parameters:
- config.identity: Identity to use for signingconfig.network
- : 'mainnet' or 'local'config.fetchRootKey
- (optional): Fetch root key (โ ๏ธ never use in production!)
---
#### agentManager.createActor
Create an actor for multiple canister calls.
`typescript
import { idlFactory } from './declarations/my-canister';
const actor = await agent.createActor({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
idlFactory,
});
const result = await actor.myMethod();
`
---
#### agentManager.query
Perform a query call (read-only, fast, no consensus).
`typescript`
const balance = await agent.query({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
methodName: 'getBalance',
args: [{ account: authClient.getPrincipalText() }],
idlFactory,
});
---
#### agentManager.update
Perform an update call (modifies state, requires consensus, costs cycles).
`typescript`
const result = await agent.update({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
methodName: 'transfer',
args: [{ to: 'recipient-principal', amount: 100 }],
idlFactory,
});
---
#### agentManager.setIdentity(identity): Promise
Update the agent's identity (useful after login/logout).
`typescript`
await agent.setIdentity(newIdentity);
---
#### generateIdentity(): Ed25519KeyIdentity
Generate a random Ed25519 identity (not recoverable).
`typescript`
const identity = generateIdentity();
const principal = identity.getPrincipal().toText();
โ ๏ธ Warning: This identity cannot be recovered if lost. Use generateIdentityWithSeed() for wallet apps.
---
#### generateIdentityWithSeed(strength?): { identity, seedPhrase }
Generate a recoverable identity with BIP39 seed phrase.
`typescript
// 12 words (128 bits)
const { identity, seedPhrase } = generateIdentityWithSeed();
// 24 words (256 bits)
const { identity, seedPhrase } = generateIdentityWithSeed(256);
`
Parameters:
- strength (optional): 128 for 12 words, 256 for 24 words (default: 128)
Returns: Object with:
- identity: Ed25519KeyIdentity instanceseedPhrase
- : BIP39 mnemonic string
---
#### fromSeedPhrase(mnemonic, accountIndex?): Ed25519KeyIdentity
Recover identity from BIP39 seed phrase.
`typescript
const identity = fromSeedPhrase('witch collapse practice feed...');
// Derive different account from same seed
const account1 = fromSeedPhrase('witch collapse...', 1);
`
Parameters:
- mnemonic: BIP39 seed phrase stringaccountIndex
- (optional): Account derivation index (default: 0)
Returns: Ed25519KeyIdentity instance
---
#### validateSeedPhrase(mnemonic): boolean
Validate a BIP39 seed phrase.
`typescript`
if (validateSeedPhrase(userInput)) {
const identity = fromSeedPhrase(userInput);
}
---
#### exportPrivateKey(identity): string
Export private key as hex string.
`typescript`
const privateKey = exportPrivateKey(identity);
// โ ๏ธ Keep this secure!
---
#### importPrivateKey(privateKeyHex): Ed25519KeyIdentity
Import identity from private key hex string.
`typescript`
const identity = importPrivateKey(privateKeyHex);
---
#### serializeIdentity(identity): SerializedIdentity
Serialize identity for storage.
`typescript`
const serialized = serializeIdentity(identity);
await saveSecureItem('wallet', serialized);
---
#### deserializeIdentity(data): Identity
Deserialize identity from storage.
`typescript`
const data = await getSecureItem('wallet');
const identity = deserializeIdentity(data);
---
#### isSecureStorageAvailable(): boolean
Check if expo-secure-store is available.
`typescript`
if (isSecureStorageAvailable()) {
await saveSecureItem('key', 'value');
}
---
#### saveSecureItem(key, value): Promise
Save to iOS Keychain / Android Keystore (or fallback to AsyncStorage).
`typescript`
await saveSecureItem('wallet_data', { principal: 'xyz...' });
---
#### getSecureItem
Get from secure storage.
`typescript`
const data = await getSecureItem('wallet_data');
---
#### removeSecureItem(key): Promise
Remove from secure storage.
`typescript`
await removeSecureItem('wallet_data');
---
`typescript
import React, { useEffect, useState } from 'react';
import { View, Button, Text, Alert } from 'react-native';
import {
createAuthClient,
generateIdentityWithSeed,
fromSeedPhrase,
validateSeedPhrase
} from 'react-native-icp-auth-kit';
export default function WalletApp() {
const [authClient, setAuthClient] = useState(null);
const [principal, setPrincipal] = useState('');
const [seedPhrase, setSeedPhrase] = useState('');
useEffect(() => {
initWallet();
}, []);
async function initWallet() {
const client = await createAuthClient();
setAuthClient(client);
// Try to restore existing wallet
const hasWallet = await client.autoLogin();
if (hasWallet) {
setPrincipal(client.getPrincipalText());
}
}
async function createNewWallet() {
// Generate wallet with 24-word seed phrase
const { identity, seedPhrase: mnemonic } = generateIdentityWithSeed(256);
await authClient.login(identity);
setPrincipal(authClient.getPrincipalText());
setSeedPhrase(mnemonic);
// Show seed phrase to user
Alert.alert(
'Save Your Seed Phrase',
Write this down:\n\n${mnemonic}\n\nYou'll need it to recover your wallet.,
[{ text: 'I Saved It' }]
);
}
async function recoverWallet() {
Alert.prompt(
'Recover Wallet',
'Enter your seed phrase:',
async (input) => {
if (!validateSeedPhrase(input.trim())) {
Alert.alert('Invalid seed phrase');
return;
}
const identity = fromSeedPhrase(input.trim());
await authClient.login(identity);
setPrincipal(authClient.getPrincipalText());
}
);
}
async function deleteWallet() {
await authClient.logout();
setPrincipal('');
setSeedPhrase('');
}
return (
{principal ? (
<>
{seedPhrase &&
>
) : (
<>
>
)}
);
}
`
`typescript
import { createAuthClient, createAgentManager } from 'react-native-icp-auth-kit';
import { idlFactory as ledgerIDL } from './declarations/ledger';
async function checkICPBalance() {
// Initialize auth
const authClient = await createAuthClient();
await authClient.autoLogin();
// Create agent
const agent = createAgentManager({
identity: authClient.getIdentity(),
network: 'mainnet',
});
// Query ICP Ledger
const balance = await agent.query({
canisterId: 'ryjl3-tyaaa-aaaaa-aaaba-cai', // ICP Ledger
methodName: 'account_balance',
args: [{
account: authClient.getIdentity().getPrincipal().toUint8Array()
}],
idlFactory: ledgerIDL,
});
console.log('ICP Balance:', balance.e8s / 100_000_000);
}
`
`typescript
import { createAgentManager } from 'react-native-icp-auth-kit';
// Start dfx locally: dfx start --clean --background
// Deploy canister: dfx deploy
const agent = createAgentManager({
identity: authClient.getIdentity(),
network: 'local', // http://localhost:4943
fetchRootKey: true, // โ ๏ธ Required for local development ONLY
});
const result = await agent.query({
canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
methodName: 'greet',
args: ['Alice'],
idlFactory,
});
`
โ ๏ธ Never use fetchRootKey: true in production! It disables certificate verification.
---
A complete working example is available in the example/ directory.
`bashClone the repository
git clone https://github.com/yourusername/react-native-icp-auth-kit.git
cd react-native-icp-auth-kit
The example demonstrates:
- โ
Creating wallets with 24-word seed phrases
- โ
Recovering wallets from seed phrases
- โ
Auto-restoring wallets on app restart
- โ
Creating ICP agents for canister interaction
- โ
Seed phrase visibility toggle with copy functionality
- โ
Wallet deletion with confirmation
Or run the example directly:
`bash
cd example
npm install
npm start
`Then press
i for iOS, a for Android, or w for web.---
Architecture
$3
`
react-native-icp-auth-kit/
โโโ src/
โ โโโ core/
โ โ โโโ identity.ts # Ed25519 key generation, BIP39 seed phrases
โ โ โโโ authClient.ts # Session management, persistence
โ โ โโโ agent.ts # HttpAgent wrapper, canister calls
โ โโโ storage/
โ โ โโโ asyncStorage.ts # AsyncStorage wrapper
โ โ โโโ secureStorage.ts # Secure storage (Keychain/Keystore)
โ โโโ index.ts # Public API exports
โโโ dist/ # Compiled JavaScript (npm package)
โโโ example/ # Example React Native app
`$3
- Ed25519 Key Generation: Uses
@noble/curves for pure JavaScript crypto
- BIP39 Seed Phrases: Implements BIP39 standard with English wordlist
- Derivation Path: m/44'/223'/0'/0/{accountIndex} (BIP44 for ICP)
- Identity Types: anonymous (2vxsx-fae) and ed25519
- Serialization: Converts keys to/from hex strings for storage$3
- Session Management: Maintains current identity state
- Auto-login: Restores identity from storage on app start
- Storage Key:
icp_auth_client in AsyncStorage
- Anonymous State: Uses principal 2vxsx-fae when logged out$3
- HttpAgent Wrapper: Simplifies
@dfinity/agent API
- Network Configs:
- Mainnet: https://ic0.app
- Local: http://localhost:4943
- Query vs Update:
- Query: Read-only, fast, no consensus
- Update: Modifies state, slower, requires consensus$3
AsyncStorage:
- Persists across app restarts
- Lost on app deletion
- All values JSON serialized
Secure Storage:
- iOS Keychain (can survive app deletion)
- Android Keystore (lost on app deletion)
- Requires
expo-secure-store---
Security Best Practices
$3
- Always show seed phrases to users and require them to back it up
- Use 24-word seed phrases for high-value wallets (256-bit entropy)
- Validate user input before recovering from seed phrases
- Clear sensitive data from memory after use
- Use secure storage for production wallet apps
- Test recovery flows thoroughly before release
$3
- Never store seed phrases in plain text in your database
- Never send seed phrases over the network
- Never use
fetchRootKey: true in production
- Never skip seed phrase backup in wallet apps
- Never auto-generate wallets without explicit user action
- Never log sensitive data (private keys, seed phrases) in production$3
For Wallet Apps:
1. Use
generateIdentityWithSeed(256) for 24-word seed phrases
2. Show seed phrase once during wallet creation
3. Require user confirmation that they saved it
4. Implement seed phrase recovery flow
5. Consider adding biometric authentication before showing keys
6. Use secure storage for identity persistenceFor dApps:
1. Use
generateIdentity() for temporary sessions
2. Save identity to AsyncStorage for session persistence
3. Allow users to export/import identities
4. Clear sessions on logout---
Troubleshooting
$3
Solution: Import polyfill at the top of your entry file:
`typescript
import 'react-native-get-random-values'; // Must be first import
`$3
Solution: Install the peer dependency:
`bash
npm install @react-native-async-storage/async-storage
cd ios && pod install && cd ..
`$3
Solution: Add NSAllowsArbitraryLoads to Info.plist for local development:
`xml
NSAppTransportSecurity
NSAllowsArbitraryLoads
`โ ๏ธ Only use this in development! For production, use proper SSL certificates.
$3
Cause: Mismatch in account index or derivation path.
Solution: Ensure you're using the same
accountIndex (default: 0):`typescript
// Creation
const { identity } = generateIdentityWithSeed();// Recovery - must use same account index
const recovered = fromSeedPhrase(seedPhrase, 0); // default is 0
`---
Performance
- Identity Generation: ~50ms (Ed25519 key generation)
- Seed Phrase Generation: ~100ms (BIP39 entropy + key derivation)
- Query Calls: ~200-500ms (network latency)
- Update Calls: ~2-5s (consensus required)
- Session Restore: ~10ms (AsyncStorage read + deserialization)
All cryptographic operations run on the JavaScript thread (no native dependencies).
---
Dependencies
$3
@dfinity/agent - HttpAgent and Actor
- @dfinity/identity - Ed25519KeyIdentity
- @dfinity/principal - Principal type
- @dfinity/candid - IDL for canister interfaces$3
@noble/curves - Ed25519 key generation (pure JS)
- @scure/bip39 - BIP39 mnemonic generation
- @scure/bip32 - HD key derivation (BIP32/BIP44)
- react-native-get-random-values - Crypto polyfill (required)$3
-
react >= 16.0.0
- react-native >= 0.60.0$3
@react-native-async-storage/async-storage - Persistent storage
- expo-secure-store - Secure storage (iOS Keychain/Android Keystore)---
Versioning
This project follows Semantic Versioning:
- Major: Breaking changes
- Minor: New features, backwards compatible
- Patch: Bug fixes, backwards compatible
---
Contributing
Contributions are welcome! Please follow these steps:
1. Fork the repository
2. Create a feature branch (
git checkout -b feature/amazing-feature)
3. Commit your changes (git commit -m 'Add amazing feature')
4. Push to the branch (git push origin feature/amazing-feature)
5. Open a Pull Request$3
`bash
Clone the repo
git clone https://github.com/yourusername/react-native-icp-auth-kit.git
cd react-native-icp-auth-kitInstall dependencies
npm installBuild the library
npm run buildRun tests
npm testWatch mode for development
npm run watch
`---
License
MIT License - see LICENSE file for details
---
Support
- GitHub Issues: Report bugs or request features
- ICP Forum: Discuss on the Internet Computer Forum
- Documentation: Internet Computer Docs
---
Acknowledgments
@dfinity libraries
- Cryptography powered by @noble and @scure`---
Built with โค๏ธ for the Internet Computer ecosystem