webauthn-server-buildkit
v1.6.0
Published
A comprehensive WebAuthn server package for TypeScript that provides secure, type-safe, and framework-independent biometric authentication
Downloads
32
Maintainers
Readme
WebAuthn Server Buildkit
📚 Documentation
- API Reference - Detailed API documentation
- Storage Adapters - Database integration examples
- Development Guide - Implementation details
- WebAuthn Specification - W3C WebAuthn standard
A comprehensive WebAuthn server package for TypeScript that provides secure, type-safe, and framework-independent biometric authentication.
Features
🔐 Security & Compliance
- Full WebAuthn Level 3 Implementation - Complete server-side implementation following the latest W3C standards
- Secure by Default - Built-in AES-256-GCM session encryption, cryptographically secure challenge generation
- Algorithm Support - ES256, RS256, EdDSA, and more COSE algorithms
- Attestation Support - Full attestation verification with multiple formats
🛠️ Developer Experience
- Framework Independent - Works with Express, Fastify, Koa, Next.js, or any Node.js framework
- Full TypeScript Support - 100% type-safe with comprehensive type definitions and strict mode
- Simple API - Intuitive methods with sensible defaults, get started in minutes
- Extensive Configuration - Every WebAuthn option is configurable with user values taking priority
📦 Integration & Storage
- Storage Agnostic - Pluggable adapter system for any database (MongoDB, PostgreSQL, Redis, etc.)
- Session Management - Built-in secure session handling with token-based authentication
- Extension Support - Full support for WebAuthn extensions
- Modern Architecture - ES2022 features, Node.js 20+ support, ESM and CommonJS builds
Installation
npm install webauthn-server-buildkit
# or
yarn add webauthn-server-buildkitQuick Start
import { WebAuthnServer, MemoryStorageAdapter } from 'webauthn-server-buildkit';
// Initialize the server
const webauthn = new WebAuthnServer({
rpName: 'My App',
rpID: 'localhost',
origin: 'http://localhost:3000',
encryptionSecret: 'your-32-character-or-longer-secret-key-here',
});
// Registration flow
async function handleRegistration(user: UserModel) {
// 1. Generate registration options
const { options, challenge } = await webauthn.createRegistrationOptions(user);
// 2. Send options to client
// ... client performs WebAuthn registration ...
// 3. Verify registration response
const { verified, registrationInfo } = await webauthn.verifyRegistration(
clientResponse,
challenge,
);
if (verified && registrationInfo) {
// Save credential to database
await saveCredential({
...registrationInfo.credential,
userId: user.id,
webAuthnUserID: options.user.id,
});
}
}
// Authentication flow
async function handleAuthentication(credentials: WebAuthnCredential[]) {
// 1. Generate authentication options
const { options, challenge } = await webauthn.createAuthenticationOptions(credentials);
// 2. Send options to client
// ... client performs WebAuthn authentication ...
// 3. Verify authentication response
const credential = credentials.find((c) => c.id === clientResponse.id);
const { verified, authenticationInfo } = await webauthn.verifyAuthentication(
clientResponse,
challenge,
credential,
);
if (verified && authenticationInfo) {
// Create session
const sessionToken = await webauthn.createSession(
credential.userId,
credential.id,
authenticationInfo.userVerified,
);
return sessionToken;
}
}Configuration
const webauthn = new WebAuthnServer({
// Required
rpName: 'My App', // Relying Party name
rpID: 'example.com', // Relying Party ID (domain)
origin: 'https://example.com', // Expected origin(s)
encryptionSecret: 'secret-key', // Min 32 chars for session encryption
// Optional
sessionDuration: 86400000, // Session duration in ms (default: 24h)
attestationType: 'none', // Attestation preference
userVerification: 'preferred', // User verification requirement
authenticatorSelection: {
// Authenticator selection criteria
residentKey: 'preferred',
userVerification: 'preferred',
authenticatorAttachment: 'platform',
},
supportedAlgorithms: [-7, -257], // COSE algorithm identifiers
challengeSize: 32, // Challenge size in bytes
timeout: 60000, // Operation timeout in ms
preferredAuthenticatorType: 'localDevice', // Preferred authenticator
storageAdapter: customAdapter, // Custom storage adapter
debug: true, // Enable debug logging
logger: customLogger, // Custom logger function
});Storage Adapters
The package includes an in-memory storage adapter for development. For production, implement your own storage adapter:
import { StorageAdapter } from 'webauthn-server-buildkit';
class MySQLStorageAdapter implements StorageAdapter {
users = {
async findById(id: string | number) {
/* ... */
},
async findByUsername(username: string) {
/* ... */
},
async create(user: Omit<UserModel, 'id'>) {
/* ... */
},
async update(id: string | number, updates: Partial<UserModel>) {
/* ... */
},
async delete(id: string | number) {
/* ... */
},
};
credentials = {
async findById(id: Base64URLString) {
/* ... */
},
async findByUserId(userId: string | number) {
/* ... */
},
async findByWebAuthnUserId(webAuthnUserId: Base64URLString) {
/* ... */
},
async create(credential: Omit<WebAuthnCredential, 'createdAt'>) {
/* ... */
},
async updateCounter(id: Base64URLString, counter: number) {
/* ... */
},
async updateLastUsed(id: Base64URLString) {
/* ... */
},
async delete(id: Base64URLString) {
/* ... */
},
async deleteByUserId(userId: string | number) {
/* ... */
},
};
challenges = {
async create(challenge: ChallengeData) {
/* ... */
},
async find(challenge: string) {
/* ... */
},
async delete(challenge: string) {
/* ... */
},
async deleteExpired() {
/* ... */
},
};
sessions = {
async create(sessionId: string, data: SessionData) {
/* ... */
},
async find(sessionId: string) {
/* ... */
},
async update(sessionId: string, data: Partial<SessionData>) {
/* ... */
},
async delete(sessionId: string) {
/* ... */
},
async deleteExpired() {
/* ... */
},
async deleteByUserId(userId: string | number) {
/* ... */
},
};
}Session Management
Built-in secure session management with encrypted tokens:
// Create session after authentication
const token = await webauthn.createSession(
userId,
credentialId,
userVerified,
{ customData: 'value' }, // Optional additional data
);
// Validate session
const { valid, sessionData } = await webauthn.validateSession(token);
// Refresh session
const newToken = await webauthn.refreshSession(token);
// Revoke session
await webauthn.revokeSession(token);
// Revoke all user sessions
await webauthn.revokeUserSessions(userId);Express.js Example
import express from 'express';
import { WebAuthnServer } from 'webauthn-server-buildkit';
const app = express();
const webauthn = new WebAuthnServer({
rpName: 'My Express App',
rpID: 'localhost',
origin: 'http://localhost:3000',
encryptionSecret: process.env.ENCRYPTION_SECRET,
});
app.use(express.json());
// Registration endpoint
app.post('/api/register/options', async (req, res) => {
const user = req.user; // From your auth middleware
// getUserCredentials should return an array of previously registered credentials for this user
// This comes from your database where you stored credentials during registration
// Each credential object should contain:
// - id: The credential ID (credentialId from registrationInfo.credential)
// - publicKey: The public key bytes (publicKey from registrationInfo.credential)
// - counter: Usage counter for replay protection (counter from registrationInfo.credential)
// - transports: Array of transport methods like ['usb', 'nfc', 'ble', 'internal']
// (transports from registrationInfo.credential or authenticator response)
// You get all this data when you save the credential after successful registration
const credentials = await getUserCredentials(user.id);
const { options } = await webauthn.createRegistrationOptions(user, credentials);
req.session.challenge = options.challenge;
res.json(options);
});
app.post('/api/register/verify', async (req, res) => {
const challenge = req.session.challenge;
const { verified, registrationInfo } = await webauthn.verifyRegistration(req.body, challenge);
if (verified && registrationInfo) {
await saveCredential(registrationInfo.credential);
res.json({ verified: true });
} else {
res.status(400).json({ verified: false });
}
});
// Authentication endpoint
app.post('/api/authenticate/options', async (req, res) => {
const credentials = await getCredentialsByUsername(req.body.username);
const { options } = await webauthn.createAuthenticationOptions(credentials);
req.session.challenge = options.challenge;
res.json(options);
});
app.post('/api/authenticate/verify', async (req, res) => {
const challenge = req.session.challenge;
const credential = await getCredentialById(req.body.id);
const { verified, authenticationInfo } = await webauthn.verifyAuthentication(
req.body,
challenge,
credential,
);
if (verified && authenticationInfo) {
const token = await webauthn.createSession(
credential.userId,
credential.id,
authenticationInfo.userVerified,
);
res.json({ verified: true, token });
} else {
res.status(401).json({ verified: false });
}
});API Reference
WebAuthnServer
Constructor
new WebAuthnServer(config: WebAuthnServerConfig)Methods
Registration
createRegistrationOptions(user, excludeCredentials?): Generate registration optionsverifyRegistration(response, challenge, origin?): Verify registration response
Authentication
createAuthenticationOptions(allowCredentials?): Generate authentication optionsverifyAuthentication(response, challenge, credential, origin?): Verify authentication response
Session Management
createSession(userId, credentialId, userVerified, additionalData?): Create sessionvalidateSession(token): Validate session tokenrefreshSession(token): Refresh session tokenrevokeSession(token): Revoke sessionrevokeUserSessions(userId): Revoke all user sessions
Utilities
cleanup(): Clean up expired datagetStorageAdapter(): Get storage adapter instance
Supported Algorithms
- ES256 (ECDSA with SHA-256) - Default
- RS256 (RSASSA-PKCS1-v1_5 with SHA-256) - Default
- ES384 (ECDSA with SHA-384)
- ES512 (ECDSA with SHA-512)
- RS384 (RSASSA-PKCS1-v1_5 with SHA-384)
- RS512 (RSASSA-PKCS1-v1_5 with SHA-512)
- PS256 (RSASSA-PSS with SHA-256)
- PS384 (RSASSA-PSS with SHA-384)
- PS512 (RSASSA-PSS with SHA-512)
Note: EdDSA (Ed25519) is defined but not yet implemented.
Security Considerations
- Encryption Secret: Use a strong, unique secret of at least 32 characters
- HTTPS Required: Always use HTTPS in production for WebAuthn
- Origin Validation: The package validates origins to prevent phishing
- Counter Tracking: Authenticator counters are tracked to detect cloned credentials
- Session Security: Sessions are encrypted with AES-256-GCM
Error Handling
The package exports typed error classes:
import {
WebAuthnError,
RegistrationError,
AuthenticationError,
VerificationError,
ConfigurationError,
StorageError,
SessionError,
} from 'webauthn-server-buildkit';
try {
await webauthn.verifyRegistration(response, challenge);
} catch (error) {
if (error instanceof RegistrationError) {
console.error('Registration failed:', error.code, error.message);
}
}Requirements
- Node.js 20.0.0 or higher
- TypeScript 5.0 or higher (for TypeScript projects)
License
MIT
👨💻 Author
Ahsan Mahmood
- Website: https://aoneahsan.com
- GitHub: @aoneahsan
- Email: [email protected]
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
