Passwordless Web3 authentication SDK with encrypted wallets and privacy features.
- Live demo: w3pk.w3hc.org
- Quick start
npm install w3pk ethersimport { createWeb3Passkey } from 'w3pk'
const w3pk = createWeb3Passkey()
// Register (auto-generates wallet and stores it securely)
const { address, username } = await w3pk.register({ username: 'alice' })
console.log('✅ Registered:', username, 'with address:', address)
// Login (for subsequent sessions)
await w3pk.login()
// Sign message
const signature = await w3pk.signMessage('Hello World')
// Derive addresses (2 modes)
const gamingWallet = await w3pk.deriveWallet('GAMING') // By tag - includes privateKey
const mainWallet = await w3pk.deriveWallet() // Auto (MAIN tag) - public address only, no privateKey
// Get RPC endpoints for any chain
const endpoints = await w3pk.getEndpoints(1) // Ethereum
const rpcUrl = endpoints[0]- 🔐 Passwordless authentication (WebAuthn/FIDO2)
- 🛡️ Origin-specific key isolation with tag-based access control
- ⏱️ Session management (in-memory + optional persistent "Remember Me")
- 🔒 Persistent sessions (encrypted with WebAuthn keys, survives page refresh)
- 🌱 HD wallet generation (BIP39/BIP44)
- 🔢 Multi-address derivation
- 🌐 Origin-specific addresses (deterministic derivation per website with tag support)
- 🥷 ERC-5564 stealth addresses (opt-in, privacy-preserving transactions with view tags)
- 🧮 ZK primitives (zero-knowledge proof generation and verification)
- 🔗 Chainlist support (2390+ networks, auto-filtered RPC endpoints)
- ⚡ EIP-7702 network detection (329+ supported networks)
- 🔍 Build verification (IPFS CIDv1 hashing for package integrity)
- 🛡️ Three-layer backup & recovery system
- Passkey auto-sync (iCloud/Google/Microsoft)
- Encrypted backups (QR codes and backup files with password protection)
- Social recovery (Shamir Secret Sharing)
// Check for existing wallet first (recommended)
const hasWallet = await w3pk.hasExistingCredential()
if (hasWallet) {
// Login to existing wallet
await w3pk.login()
} else {
// Register new wallet (generates and stores wallet securely)
const { address, username } = await w3pk.register({ username: 'alice' })
}
// Advanced: List all wallets on device
const wallets = await w3pk.listExistingCredentials()
wallets.forEach(w => console.log(w.username, w.ethereumAddress))
// Logout
await w3pk.logout()
// Status
w3pk.isAuthenticated
w3pk.userImportant: Backup your wallet!
// Create encrypted backups:
const zipBackup = await w3pk.createZipBackup('strong-password')
const qrBackup = await w3pk.createQRBackup('optional-password')SECURITY MODEL: deriveWallet() supports two secure modes:
// 1. MAIN tag (default) - ADDRESS ONLY, NO PRIVATE KEY
const mainWallet = await w3pk.deriveWallet()
// Returns: { address, index, origin, tag: 'MAIN' }
// ✅ Safe for display
// ❌ No privateKey exposed
// 2. Custom tag - INCLUDES PRIVATE KEY for app-specific use
const gamingWallet = await w3pk.deriveWallet('GAMING')
const funWallet = await w3pk.deriveWallet('FUN')
const basicWallet = await w3pk.deriveWallet('BASIC')
// Returns: { address, privateKey, index, origin, tag }
// Different tags = different addresses
console.log(mainWallet.address !== gamingWallet.address) // true
console.log(gamingWallet.address !== tradingWallet.address) // true
// SECURITY: Applications CANNOT access master mnemonic
// await w3pk.exportMnemonic() // ❌ Throws error
// Sign message (works with any address - no key exposure needed)
const signature = await w3pk.signMessage('Hello World')By default, after authentication, operations work for 1 hour without repeated biometric prompts.
// Configure in-memory session duration
const w3pk = createWeb3Passkey({
sessionDuration: 2 // 2 hours (default: 1, cleared on page refresh)
})
// After login, mnemonic is cached in memory
await w3pk.login()
// These operations use the cached session
await w3pk.deriveWallet('GAMING')
await w3pk.signMessage('Hello')
// Session cleared on page refresh// Enable persistent sessions (survives page refresh)
const w3pk = createWeb3Passkey({
sessionDuration: 1, // In-memory session (1 hour)
persistentSession: {
enabled: true, // Enable "Remember Me"
duration: 168, // 7 days (in hours)
requireReauth: true // Prompt on page refresh (secure)
}
})
// Auto-restore mode (no prompt on refresh)
const w3pkAutoRestore = createWeb3Passkey({
persistentSession: {
enabled: true,
duration: 30 * 24, // 30 days
requireReauth: false // Silent restore
}
})
// Check session status
w3pk.hasActiveSession() // true
w3pk.getSessionRemainingTime() // seconds remaining
// Extend session
w3pk.extendSession()
// Clear session (clears both RAM and persistent)
await w3pk.clearSession()
// Disable sessions (most secure - prompt every time)
const w3pkNoSessions = createWeb3Passkey({
sessionDuration: 0,
persistentSession: { enabled: false }
})Security Modes & Persistence:
- STANDARD mode: Persistent sessions ✅ allowed
- YOLO mode: Persistent sessions ✅ allowed
- STRICT mode: Persistent sessions ❌ NEVER allowed
Even with an active session, you can require fresh biometric authentication for specific operations:
// Session is active, but force authentication anyway
await w3pk.exportMnemonic({ requireAuth: true })
await w3pk.signMessage('Transfer $1000', { requireAuth: true })
await w3pk.deriveWallet(5, { requireAuth: true })
await w3pk.stealth.getKeys({ requireAuth: true })
// Example: Require auth for high-value transactions
async function transferFunds(amount: number, recipient: string) {
// For transfers above $100, require fresh authentication
const requireAuth = amount > 100
const signature = await w3pk.signMessage(
`Transfer ${amount} to ${recipient}`,
{ requireAuth }
)
// ... submit transaction
}// Get public RPC endpoints
const endpoints = await w3pk.getEndpoints(1) // Ethereum
const rpcUrl = endpoints[0]
// Other chains
await w3pk.getEndpoints(10) // Optimism
await w3pk.getEndpoints(42161) // Arbitrum
await w3pk.getEndpoints(8453) // Base
// Use with ethers.js
import { ethers } from 'ethers'
const endpoints = await w3pk.getEndpoints(137)
const provider = new ethers.JsonRpcProvider(endpoints[0])
const blockNumber = await provider.getBlockNumber()// Check network support
const supported = await w3pk.supportsEIP7702(1) // true
// Configure RPC testing
await w3pk.supportsEIP7702(999, {
maxEndpoints: 5,
timeout: 5000
})const w3pk = createWeb3Passkey({
stealthAddresses: {}
})
// Get stealth meta-address
const metaAddress = await w3pk.stealth?.getStealthMetaAddress()
// Generate stealth address
const announcement = await w3pk.stealth?.generateStealthAddress()
// Parse announcement
const result = await w3pk.stealth?.parseAnnouncement({
stealthAddress: announcement.stealthAddress,
ephemeralPublicKey: announcement.ephemeralPublicKey,
viewTag: announcement.viewTag
})
if (result.isForUser) {
// Use private key to spend funds
console.log('Private key:', result.stealthPrivateKey)
}
// Scan announcements
const myPayments = await w3pk.stealth?.scanAnnouncements(announcements)import { isStrongPassword } from 'w3pk'
// Validate password strength before creating backups
const password = 'MyS3cur3!Password@2042'
if (!isStrongPassword(password)) {
throw new Error('Password does not meet security requirements')
}
// Requirements: 12+ chars, uppercase, lowercase, number, special char, not common
// Get backup status
const status = await w3pk.getBackupStatus()
console.log('Security Score:', status.securityScore.score) // 0-100
// Create QR backup
const { qrCodeDataURL } = await w3pk.createQRBackup('password')
// Display QR code or save as image
// Setup social recovery (3-of-5 guardians)
await w3pk.setupSocialRecovery(
[
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', phone: '+1234567890' },
{ name: 'Charlie' }
],
3 // threshold
)
// Generate guardian invite
const invite = await w3pk.generateGuardianInvite(guardianId)
// Share invite.qrCode or invite.shareCode with guardian
// Recover from guardian shares
const { mnemonic } = await w3pk.recoverFromGuardians([
share1, share2, share3
])
// Restore from backup
await w3pk.restoreFromBackup(encryptedData, password)
// Simulate recovery scenarios for testing
const result = await w3pk.simulateRecoveryScenario({
type: 'lost-device',
hasBackup: true,
hasSocialRecovery: true
})
console.log('Can recover:', result.canRecover)See Recovery Guide for complete documentation.
import { getCurrentBuildHash, verifyBuildHash } from 'w3pk'
// Get IPFS hash of installed w3pk build
const hash = await getCurrentBuildHash()
console.log('Build hash:', hash)
// => bafybeiecegenbzltuaiel3i6z3azesl6y32ugicavyvasfeyddsbnuhzkq
// Verify against trusted hash (from GitHub releases)
const trusted = 'bafybeiecegenbzltuaiel3i6z3azesl6y32ugicavyvasfeyddsbnuhzkq'
const isValid = await verifyBuildHash(trusted)
if (isValid) {
console.log('✅ Build integrity verified!')
}See Build Verification Guide for complete documentation.
bafybeiecegenbzltuaiel3i6z3azesl6y32ugicavyvasfeyddsbnuhzkq
Verify package integrity:
import { verifyBuildHash } from 'w3pk'
const TRUSTED_HASH = 'bafybeiecegenbzltuaiel3i6z3azesl6y32ugicavyvasfeyddsbnuhzkq'
const isValid = await verifyBuildHash(TRUSTED_HASH)
if (!isValid) {
throw new Error('Package integrity check failed!')
}Multi-source verification:
- GitHub: Check release notes for official hash
- On-chain: Verify via DAO-maintained registry (coming soon)
- Local build:
pnpm build && pnpm build:hash
See Build Verification Guide for complete documentation.
- Quick Start Guide - Get started in 5 minutes
- Integration Guidelines - Best practices for production apps
- API Reference - Complete API documentation
- Build Verification - Package integrity verification
- Security Architecture - Security model and threat analysis
- Recovery & Backup System - Three-layer backup architecture
- ZK Proofs - Zero-Knowledge cryptography utilities
- Browser compatibility
We welcome contributions! See CONTRIBUTING.md for guidelines.
GPL-3.0
Julien Béranger (GitHub)
- Element: @julienbrg:matrix.org
- Farcaster: julien-
- Telegram: @julienbrg
- Twitter: @julienbrg