@productiongrade/passkeys
v1.1.1
Published
Production-ready passkey authentication library for Node.js applications
Maintainers
Readme
@productiongrade/passkeys
Production-ready passkey authentication library for Node.js applications.
Why Passkeys?
Passkeys provide passwordless authentication using device biometrics (Face ID, Touch ID, Windows Hello) or security keys. Benefits include:
- More Secure: No passwords to steal, phish, or leak
- Faster UX: Biometric auth in seconds, no typing
- Lower Costs: Eliminate password reset support tickets
- Standards-Based: WebAuthn/FIDO2 supported across all major browsers
- Cross-Device: Works on phones, laptops, tablets, and security keys
Features
Core Capabilities
- WebAuthn/FIDO2 Compliant - Standards-based passkey authentication
- Next.js 14+ Adapter - App Router integration with 10-20 lines of code
- Express Integration - Ready-to-use Express adapter with RFC 7807 error handling
- NextAuth Provider - Seamless NextAuth credentials provider for session management
- React Hooks -
usePasskeyRegistration,usePasskeyAuth,usePasskeyManagement - Framework-Agnostic Core - Bring your own framework
- Flexible Storage - Use any database via storage interface
- TypeScript-First - 100% type-safe with full IntelliSense support
Security Features
- Counter Anomaly Detection - Detects cloned credentials
- Challenge-Based Auth - Replay attack prevention (5-min TTL)
- Recovery Mechanisms - Bcrypt-hashed recovery codes + email recovery
- Security Event Hooks - Monitor all authentication events
- User Verification - Configurable biometric requirements
Developer Experience
- 30-Minute Integration - Get started in minutes
- Reference Implementations - Prisma (PostgreSQL) + Redis adapters included
- Comprehensive Docs - API reference, migration guides, troubleshooting
- 93%+ Test Coverage - Production-ready quality with 160+ tests
- Testing Utilities - Mock storage and fixtures for easy testing
- Type-Safe Error Handling - Type guards and enhanced error messages
- Configuration Validation - Catch config errors early with helpful messages
- Active Maintenance - Built and maintained by ProductionGrade
Installation
npm install @productiongrade/passkeysPeer Dependencies
# For Next.js integration
npm install next next-auth # Optional: next-auth only if using PasskeyProvider
# For Express integration
npm install express
# For React hooks
npm install react @simplewebauthn/browserQuick Start
Next.js 14+ (App Router)
// app/api/auth/passkeys/[[...passkey]]/route.ts
import { createNextPasskeys } from '@productiongrade/passkeys/nextjs';
import { PrismaAdapter } from '@productiongrade/passkeys/adapters/prisma';
import { prisma } from '@/lib/prisma';
const { POST, GET, DELETE, PATCH } = createNextPasskeys({
rpId: process.env.NEXT_PUBLIC_RP_ID!,
rpName: 'My App',
origin: process.env.NEXT_PUBLIC_APP_URL!,
storage: new PrismaAdapter(prisma),
});
export { POST, GET, DELETE, PATCH };With NextAuth Integration:
// app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import { PasskeyProvider } from '@productiongrade/passkeys/nextjs/provider';
import { storage } from '@/lib/storage';
const handler = NextAuth({
providers: [
PasskeyProvider({
rpId: process.env.NEXT_PUBLIC_RP_ID!,
rpName: 'My App',
origin: process.env.NEXT_PUBLIC_APP_URL!,
storage,
}),
],
});
export { handler as GET, handler as POST };Express
import express from 'express';
import { createPasskeys } from '@productiongrade/passkeys/express';
import { MemoryStorage, MemoryChallengeStorage } from '@productiongrade/passkeys/adapters/memory';
const app = express();
app.use(express.json());
// Create passkey authentication
const { router, middleware } = createPasskeys({
rpId: 'localhost', // Your domain
rpName: 'My App', // Your app name
origin: 'http://localhost:3000', // Your origin
storage: new MemoryStorage(), // User/passkey storage
challengeStorage: new MemoryChallengeStorage(), // Challenge storage
recovery: {
email: {
enabled: true,
sendEmail: async (to, token) => {
// Send recovery email
console.log(`Recovery token for ${to}: ${token}`);
}
}
},
hooks: {
onAuthSuccess: async (userId) => {
console.log('User authenticated:', userId);
}
}
});
// Mount authentication routes
app.use('/api/auth', router);
// Protected route example
app.get('/api/profile', middleware.requireAuth, (req, res) => {
res.json({ user: req.user });
});
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});React Integration
import { usePasskeyAuth } from '@productiongrade/passkeys/react';
function LoginForm() {
const { authenticate, isLoading, error } = usePasskeyAuth({
apiUrl: '/api/auth',
onSuccess: (user) => {
console.log('Logged in:', user);
navigate('/dashboard');
},
});
return (
<button onClick={() => authenticate('[email protected]')} disabled={isLoading}>
{isLoading ? 'Authenticating...' : 'Sign in with Passkey'}
</button>
);
}Available Hooks:
usePasskeyRegistration()- Passkey registration flowusePasskeyAuth()- Authentication flowusePasskeyManagement()- List/update/delete passkeys
See react-integration.mdx for complete React documentation.
Documentation
Getting Started
- Getting Started - Quick setup guide for all frameworks
- Concepts - Understanding passkeys, WebAuthn, and architecture
- Examples - Common patterns and copy-paste code samples
Reference
- API Reference - Complete API documentation for all services
- Configuration - All configuration options and settings
Guides
- Best Practices - Security, performance, and production recommendations
- Deployment - Production deployment checklist and setup
- Testing - Testing strategies, patterns, and utilities
- Error Handling - Error types, handling patterns, and recovery
- Patterns - Real-world patterns (multi-tenant, RBAC, sessions, etc.)
Framework Integration
- React Integration - Frontend integration with React hooks
- Storage Adapters - Implement custom storage for any database
- Migration Guide - Migrate from password-based auth
- Troubleshooting - Common issues and solutions
Storage Adapters
Use any database by implementing the PasskeyStorage interface:
import { PrismaAdapter } from '@productiongrade/passkeys/adapters/prisma';
import { RedisChallengeStorage } from '@productiongrade/passkeys/adapters/redis';
import prisma from './prisma';
import redis from './redis';
const { router } = createPasskeys({
rpId: 'example.com',
rpName: 'My App',
origin: 'https://example.com',
storage: new PrismaAdapter(prisma), // PostgreSQL via Prisma
challengeStorage: new RedisChallengeStorage(redis), // Redis for challenges
});Included Adapters:
MemoryStorage- In-memory (development only)PrismaAdapter- PostgreSQL, MySQL, SQLite via PrismaRedisChallengeStorage- Redis for ephemeral challenges
Custom Adapters: See storage-adapters.mdx for implementing your own storage adapter.
Examples
React Full-Stack Example
examples/react-fullstack-app/ - Complete working application
A production-ready example demonstrating:
- Passkey registration and authentication
- React hooks (
usePasskeyRegistration,usePasskeyAuth,usePasskeyManagement) - Express backend with session management
- Responsive, modern UI
- TypeScript throughout
cd examples/react-fullstack-app
npm install
npm run devOpen http://localhost:5173 to see it in action!
Basic Express Example
examples/basic-express-app.ts - Minimal Express server
npm run build
npx ts-node examples/basic-express-app.tsDeveloper Utilities
Type-Safe Error Handling
import { isAuthenticationError, getErrorDetails } from '@productiongrade/passkeys';
try {
await passkeyService.authenticate(...);
} catch (error) {
if (isAuthenticationError(error)) {
const details = getErrorDetails(error);
if (details?.details?.troubleshooting) {
console.log('Help:', details.details.troubleshooting);
}
}
}Configuration Validation
import { validatePasskeyConfig, createPasskeyConfig } from '@productiongrade/passkeys';
// Validate config at startup
const result = validatePasskeyConfig(config);
if (!result.valid) {
console.error('Errors:', result.errors);
}
// Or use builder with defaults
const config = createPasskeyConfig({
rpId: 'example.com',
rpName: 'My App',
origin: 'https://example.com',
storage: myStorage,
challenges: myChallengeStorage,
});Testing Utilities
import { createMockStorage, testFixtures } from '@productiongrade/passkeys/testing';
const storage = createMockStorage();
const user = testFixtures.createTestUser({ email: '[email protected]' });Debug Mode
import { debugLog } from '@productiongrade/passkeys';
// Set DEBUG=@productiongrade/passkeys:* to enable
debugLog('PasskeyService', 'Starting registration', { userId, email });See api-reference.mdx for complete utility documentation.
Testing
# Run all tests (160+ tests, 93%+ coverage)
npm test
# Watch mode for development
npm run test:watch
# Generate coverage report
npm run test:coverage
# Test API endpoints
chmod +x examples/test-api.sh
./examples/test-api.shTest Coverage:
- Unit tests for all core services
- Integration tests for complete authentication flows
- Contract tests for storage implementations
- Real WebAuthn testing with HTTPS
See TESTING.md for detailed testing instructions.
Architecture
- Framework-agnostic core - works with any Node.js framework
- Storage interface pattern - works with any database
- Express adapter - ready-to-use Express integration
- React hooks - optional frontend utilities
- Reference implementations - Prisma, Redis, in-memory examples
Requirements
- Node.js 18+ (LTS)
- TypeScript 5.3+ (recommended)
Security
This library implements WebAuthn/FIDO2 standards with enterprise-grade security:
Authentication Security
- Challenge-based authentication (5-minute TTL, single-use)
- Origin validation (prevents phishing attacks)
- Counter anomaly detection (detects cloned credentials)
- User verification support (biometric/PIN requirements)
Data Security
- Bcrypt-hashed recovery codes (10 rounds)
- SHA-256 hashed email recovery tokens
- No plaintext secrets ever stored
- Automatic challenge cleanup
Monitoring & Compliance
- Security event hooks for all authentication events
- Counter anomaly alerts
- Failed authentication tracking
- RFC 7807 Problem Details error responses
Example: Security Monitoring
const { router } = createPasskeys({
// ... config
hooks: {
onAuthFailure: async (email, error) => {
await securityLog.create({
type: 'AUTH_FAILURE',
email,
error: error.message,
});
},
onCounterAnomaly: async (userId, passkeyId, expected, received) => {
await alerts.send('CRITICAL: Possible cloned credential', {
userId,
passkeyId,
expected,
received,
});
// Lock account pending investigation
await accounts.lock(userId);
},
},
});Contributing
We welcome contributions! See CONTRIBUTING.md for:
- Development setup
- Code standards
- Testing requirements
- Pull request process
Changelog
See CHANGELOG.md for release history.
License
MIT © ProductionGrade
Support
Getting Help
- Documentation: Complete docs - API reference, guides, and examples
- Bug Reports: GitHub Issues
- Discussions: GitHub Discussions
- Email: [email protected]
- Troubleshooting: Common issues
Resources
- Migration Guide: migration.mdx - Step-by-step migration from passwords
- API Reference: api-reference.mdx - Complete API documentation
- React Guide: react-integration.mdx - Frontend integration
Roadmap
Future enhancements:
- Fastify adapter
- Vue.js composables
- Svelte stores
- MongoDB adapter
- MySQL/PostgreSQL adapters (non-Prisma)
- Enhanced audit logging
Built by ProductionGrade
Stay Ahead on Production grade Security, Engineering and AI Governance
I write weekly technical breakdowns for engineering leaders — covering security, infrastructure, and AI governance. No fluff. Just what works in production.
- Join the newsletter https://newsletter.productiongrade.tech for weekly guides + bonus resources
- Follow me on Medium https://blog.productiongrade.tech for deep-dive articles
