npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-icp-auth-kit

v0.1.0

Published

Internet Computer Protocol (ICP) authentication and agent library for React Native

Readme

React Native ICP Auth Kit

Identity management and authentication library for Internet Computer Protocol (ICP) in React Native

npm version License: MIT TypeScript React Native

FeaturesInstallationQuick StartDocumentationExample


Overview

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.


Features

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


Installation

npm install react-native-icp-auth-kit

Required Peer Dependencies

npm install react-native-get-random-values
npm install @react-native-async-storage/async-storage

Optional Dependencies

For secure storage (iOS Keychain/Android Keystore):

npm install expo-secure-store

iOS Setup

cd ios && pod install && cd ..

Quick Start

1. Setup Crypto Polyfill

Add this to your app's entry file (e.g., index.js or App.tsx):

import 'react-native-get-random-values'; // Must be first import
import { createAuthClient, generateIdentityWithSeed } from 'react-native-icp-auth-kit';

2. Create Your First Wallet

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);
}

3. Restore Session on App Restart

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');
  }
}

4. Interact with Canisters

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,
  });
}

Documentation

Table of Contents


Wallet Recovery Methods

🔑 Seed Phrase Recovery (Recommended)

Industry standard used by MetaMask, Trust Wallet, and most cryptocurrency wallets.

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.

🔐 Private Key Export/Import

For advanced users who prefer manual key management.

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.

🛡️ Secure Storage (iOS Keychain/Android Keystore)

Automatic backup that survives app deletion (iOS only with proper configuration).

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

Comparison

| 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.


API Reference

Auth Client API

createAuthClient(config?): Promise<RNAuthClient>

Creates an authenticated client instance.

const authClient = await createAuthClient();

Parameters:

  • config (optional): Configuration object
    • storage (optional): Custom storage implementation

Returns: Promise resolving to RNAuthClient instance


authClient.login(identity): Promise<Identity>

Login with an Ed25519 identity and persist to storage.

const { identity } = generateIdentityWithSeed();
await authClient.login(identity);

Parameters:

  • identity: Ed25519KeyIdentity instance

Returns: Promise resolving to the saved identity


authClient.autoLogin(): Promise<boolean>

Attempts to restore session from storage automatically.

const hasSession = await authClient.autoLogin();
if (hasSession) {
  console.log('Session restored!');
}

Returns: true if session was restored, false otherwise


authClient.logout(): Promise<void>

Logout and clear all stored identity data.

await authClient.logout();

authClient.isAuthenticated(): boolean

Check if user is currently authenticated.

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.

const identity = authClient.getIdentity();

authClient.getPrincipal(): Principal

Get the current principal.

const principal = authClient.getPrincipal();
console.log(principal.toText());

authClient.getPrincipalText(): string

Get principal as text string.

const principalText = authClient.getPrincipalText();
// Example: "2vxsx-fae" (anonymous) or "hpikg-6exdt-jn33w-ndty3-fc7jc-tl2lr-buih3-cs3y7-tftkp-sfp62-gqe"

Agent Manager API

createAgentManager(config): AgentManager

Creates an agent manager for canister interactions.

const agent = createAgentManager({
  identity: authClient.getIdentity(),
  network: 'mainnet', // or 'local'
  fetchRootKey: false, // true ONLY for local development
});

Parameters:

  • config.identity: Identity to use for signing
  • config.network: 'mainnet' or 'local'
  • config.fetchRootKey (optional): Fetch root key (⚠️ never use in production!)

agentManager.createActor<T>(options): Promise<ActorSubclass<T>>

Create an actor for multiple canister calls.

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<T>(options): Promise<T>

Perform a query call (read-only, fast, no consensus).

const balance = await agent.query({
  canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
  methodName: 'getBalance',
  args: [{ account: authClient.getPrincipalText() }],
  idlFactory,
});

agentManager.update<T>(options): Promise<T>

Perform an update call (modifies state, requires consensus, costs cycles).

const result = await agent.update({
  canisterId: 'rrkah-fqaaa-aaaaa-aaaaq-cai',
  methodName: 'transfer',
  args: [{ to: 'recipient-principal', amount: 100 }],
  idlFactory,
});

agentManager.setIdentity(identity): Promise<void>

Update the agent's identity (useful after login/logout).

await agent.setIdentity(newIdentity);

Identity Utilities API

generateIdentity(): Ed25519KeyIdentity

Generate a random Ed25519 identity (not recoverable).

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.

// 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 instance
  • seedPhrase: BIP39 mnemonic string

fromSeedPhrase(mnemonic, accountIndex?): Ed25519KeyIdentity

Recover identity from BIP39 seed phrase.

const identity = fromSeedPhrase('witch collapse practice feed...');

// Derive different account from same seed
const account1 = fromSeedPhrase('witch collapse...', 1);

Parameters:

  • mnemonic: BIP39 seed phrase string
  • accountIndex (optional): Account derivation index (default: 0)

Returns: Ed25519KeyIdentity instance


validateSeedPhrase(mnemonic): boolean

Validate a BIP39 seed phrase.

if (validateSeedPhrase(userInput)) {
  const identity = fromSeedPhrase(userInput);
}

exportPrivateKey(identity): string

Export private key as hex string.

const privateKey = exportPrivateKey(identity);
// ⚠️ Keep this secure!

importPrivateKey(privateKeyHex): Ed25519KeyIdentity

Import identity from private key hex string.

const identity = importPrivateKey(privateKeyHex);

serializeIdentity(identity): SerializedIdentity

Serialize identity for storage.

const serialized = serializeIdentity(identity);
await saveSecureItem('wallet', serialized);

deserializeIdentity(data): Identity

Deserialize identity from storage.

const data = await getSecureItem('wallet');
const identity = deserializeIdentity(data);

Secure Storage API

isSecureStorageAvailable(): boolean

Check if expo-secure-store is available.

if (isSecureStorageAvailable()) {
  await saveSecureItem('key', 'value');
}

saveSecureItem(key, value): Promise<void>

Save to iOS Keychain / Android Keystore (or fallback to AsyncStorage).

await saveSecureItem('wallet_data', { principal: 'xyz...' });

getSecureItem<T>(key): Promise<T | null>

Get from secure storage.

const data = await getSecureItem('wallet_data');

removeSecureItem(key): Promise<void>

Remove from secure storage.

await removeSecureItem('wallet_data');

Complete Examples

Basic Wallet App

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 (
    <View style={{ padding: 20 }}>
      {principal ? (
        <>
          <Text>Principal: {principal}</Text>
          {seedPhrase && <Text>Seed: {seedPhrase}</Text>}
          <Button title="Delete Wallet" onPress={deleteWallet} color="red" />
        </>
      ) : (
        <>
          <Button title="Create New Wallet" onPress={createNewWallet} />
          <Button title="Recover Wallet" onPress={recoverWallet} />
        </>
      )}
    </View>
  );
}

Canister Interaction Example

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);
}

Local Development with dfx

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.


Example App

A complete working example is available in the example/ directory.

# Clone the repository
git clone https://github.com/yourusername/react-native-icp-auth-kit.git
cd react-native-icp-auth-kit

# Install library dependencies
npm install

# Build the library
npm run build

# Run the example app
npm run example

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:

cd example
npm install
npm start

Then press i for iOS, a for Android, or w for web.


Architecture

Core Components

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

1. Identity Management (identity.ts)

  • 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

2. Auth Client (authClient.ts)

  • 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. Agent Manager (agent.ts)

  • 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

4. Storage Layers

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

✅ DO

  • 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

❌ DON'T

  • 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

🔒 Recommended Practices

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 persistence

For 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

Error: "crypto.getRandomValues() not supported"

Solution: Import polyfill at the top of your entry file:

import 'react-native-get-random-values'; // Must be first import

Error: "Unable to resolve module @react-native-async-storage/async-storage"

Solution: Install the peer dependency:

npm install @react-native-async-storage/async-storage
cd ios && pod install && cd ..

Error: "Network request failed" on iOS

Solution: Add NSAllowsArbitraryLoads to Info.plist for local development:

<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>

⚠️ Only use this in development! For production, use proper SSL certificates.

Seed phrase recovery returns different principal

Cause: Mismatch in account index or derivation path.

Solution: Ensure you're using the same accountIndex (default: 0):

// 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

Core ICP Libraries

Cryptography

React Native (Peer Dependencies)

  • react >= 16.0.0
  • react-native >= 0.60.0

Optional


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

Development Setup

# Clone the repo
git clone https://github.com/yourusername/react-native-icp-auth-kit.git
cd react-native-icp-auth-kit

# Install dependencies
npm install

# Build the library
npm run build

# Run tests
npm test

# Watch mode for development
npm run watch

License

MIT License - see LICENSE file for details


Support


Acknowledgments


Built with ❤️ for the Internet Computer ecosystem

Report Bug · Request Feature