@bernierllc/crypto-utils
v1.0.6
Published
Comprehensive cryptographic utilities including key generation, JWT tokens, magic links, and secure hashing for BernierLLC ecosystem
Readme
@bernierllc/crypto-utils
Comprehensive cryptographic utilities including key generation, JWT tokens, magic links, and secure hashing for modern applications.
Installation
npm install @bernierllc/crypto-utilsQuick Start
import {
generateApiKey,
generateAccessToken,
generateEmailMagicLink,
verifyJWT,
constantTimeCompare
} from '@bernierllc/crypto-utils';
// Generate an API key with sensible defaults
const apiKey = await generateApiKey();
console.log(apiKey); // ak_a1b2c3d4e5f6...
// Generate JWT access token
const accessToken = generateAccessToken({
sub: 'user123',
email: '[email protected]'
}, 'your-secret');
// Generate magic link for email authentication
const magicLink = generateEmailMagicLink('user123', '[email protected]', 'your-secret');
// Secure password verification
const isValid = constantTimeCompare(providedPassword, storedPasswordHash);Core Features
🔐 Key Generation
- Multiple Algorithms: Random bytes, UUID v4, custom patterns
- Multiple Encodings: Hex, Base64, Base58 (Bitcoin-style)
- Security Features: Entropy validation, collision detection, safe hashing
🎟️ JWT Tokens
- Standards Compliant: RFC 7519 JWT implementation
- Multiple Algorithms: HS256, HS384, HS512
- Claims Validation: Expiration, issuer, audience, subject
✉️ Magic Links
- Email Authentication: Secure passwordless login
- Password Reset: Time-limited reset tokens
- URL-Safe: Base64URL encoding for web compatibility
- TOTP Support: Time-based one-time passwords
🛡️ Security Utilities
- Constant-Time Comparison: Prevent timing attacks
- Password Verification: Secure hash validation
- Token Validation: Comprehensive security checks
API Reference
Key Generation
Core Generation Functions
generateRandomKey(options?)
Generate cryptographically secure random keys using crypto.randomBytes.
import { generateRandomKey } from '@bernierllc/crypto-utils';
const result = await generateRandomKey({
length: 32, // Key length in bytes
prefix: 'ak_', // Optional prefix
encoding: 'hex', // 'hex' | 'base64' | 'base58'
validateEntropy: true, // Validate entropy requirements
minEntropyBits: 128, // Minimum entropy in bits
checkUnique: async (key) => !await db.keyExists(key), // Collision check
maxRetries: 5 // Max collision retry attempts
});
console.log(result.key); // Generated key string
console.log(result.entropy); // Estimated entropy in bits
console.log(result.metadata); // Generation metadataConvenience Functions
Pre-configured key generators with sensible defaults:
import {
generateApiKey, // ak_ + 32 bytes + hex
generateSecretKey, // sk_ + 48 bytes + base64
generateSessionToken, // sess_ + UUID + base58
generateLicenseKey // lic_ + pattern XXXX-XXXX-XXXX-XXXX
} from '@bernierllc/crypto-utils';
const apiKey = await generateApiKey(); // ak_a1b2c3...
const secret = await generateSecretKey(); // sk_SGVsbG8...
const session = await generateSessionToken(); // sess_23456789...
const license = await generateLicenseKey(); // lic_A1B2-C3D4...JWT Tokens
Token Generation
generateJWT(payload, secret, options?)
Generate signed JWT tokens with custom claims.
import { generateJWT } from '@bernierllc/crypto-utils';
const token = generateJWT({
sub: 'user123',
email: '[email protected]',
role: 'admin'
}, 'your-jwt-secret', {
algorithm: 'HS256', // HS256 | HS384 | HS512
expiresIn: 3600, // 1 hour in seconds
issuer: 'your-app',
audience: 'api',
includeIssuedAt: true // Include iat claim
});verifyJWT(token, secret, options?)
Verify and validate JWT tokens.
import { verifyJWT } from '@bernierllc/crypto-utils';
const result = verifyJWT(token, 'your-jwt-secret', {
issuer: 'your-app', // Expected issuer
audience: 'api', // Expected audience
clockTolerance: 30, // Allow 30 seconds clock skew
ignoreExpiration: false // Enforce expiration
});
if (result.valid) {
console.log('User:', result.payload?.sub);
console.log('Expires in:', result.expiresIn, 'seconds');
} else {
console.error('Invalid token:', result.error);
}Convenience Token Functions
import {
generateAccessToken, // 1 hour expiry, HS256
generateRefreshToken, // 7 days expiry, HS256
} from '@bernierllc/crypto-utils';
// Generate access token (1 hour expiry)
const accessToken = generateAccessToken({
sub: 'user123',
email: '[email protected]'
}, 'your-secret');
// Generate refresh token (7 days expiry)
const refreshToken = generateRefreshToken({
sub: 'user123'
}, 'your-secret');Magic Links
Email Authentication Links
generateMagicLink(userId, email, secret, options?)
Generate secure magic links for passwordless authentication.
import { generateMagicLink } from '@bernierllc/crypto-utils';
const result = generateMagicLink('user123', '[email protected]', 'your-secret', {
expirationMinutes: 15, // Link expires in 15 minutes
action: 'login', // Action type
customData: { // Additional data
ip: '192.168.1.1',
userAgent: 'Mozilla/5.0...'
}
});
const magicUrl = `https://yourapp.com/auth/verify?token=${result.token}`;
console.log('Magic link expires at:', new Date(result.expiresAt));verifyMagicLink(token, secret, options?)
Verify magic link tokens.
import { verifyMagicLink } from '@bernierllc/crypto-utils';
const result = verifyMagicLink(token, 'your-secret');
if (result.valid) {
console.log('User ID:', result.payload?.userId);
console.log('Email:', result.payload?.email);
console.log('Action:', result.payload?.action);
console.log('Remaining time:', result.remainingMinutes, 'minutes');
// Authenticate user
await authenticateUser(result.payload.userId);
} else {
console.error('Invalid magic link:', result.error);
}Convenience Magic Link Functions
import {
generateEmailMagicLink, // 15 min expiry, email_auth action
generatePasswordResetLink // 30 min expiry, password_reset action
} from '@bernierllc/crypto-utils';
// Email verification link
const emailLink = generateEmailMagicLink('user123', '[email protected]', 'secret');
// Password reset link
const resetLink = generatePasswordResetLink('user123', '[email protected]', 'secret');One-Time Passwords (OTP)
import { generateOTP, generateTOTP, verifyTOTP } from '@bernierllc/crypto-utils';
// Simple numeric OTP
const otp = generateOTP(6); // 123456
// Time-based OTP (Google Authenticator compatible)
const secret = '48656c6c6f21deadbeef'; // Hex-encoded secret
const totp = generateTOTP(secret, {
timeStep: 30, // 30-second windows
digits: 6, // 6-digit codes
algorithm: 'sha1' // sha1 | sha256 | sha512
});
// Verify TOTP code
const isValid = verifyTOTP(userProvidedCode, secret, {
window: 1 // Allow ±1 time window (90 seconds total)
});Security Utilities
Constant-Time Comparison
Prevent timing attacks when comparing sensitive values.
import {
constantTimeCompare,
constantTimeCompareHex,
validateSecureToken
} from '@bernierllc/crypto-utils';
// Basic constant-time string comparison
const isMatch = constantTimeCompare(providedToken, expectedToken);
// Hex comparison (case-insensitive)
const hexMatch = constantTimeCompareHex('deadbeef', 'DEADBEEF'); // true
// Secure token validation with options
const isValid = validateSecureToken(providedToken, expectedToken, {
caseSensitive: false, // Case-insensitive comparison
trimWhitespace: true, // Trim whitespace
maxLength: 1000 // Maximum token length
});Password Hash Verification
import { verifyPasswordHash } from '@bernierllc/crypto-utils';
// Custom hash function (use bcrypt, scrypt, etc. in production)
const hashPassword = (password: string) => {
return crypto.createHash('sha256').update(password + 'salt').digest('hex');
};
const isValidPassword = verifyPasswordHash(
providedPassword,
storedPasswordHash,
hashPassword
);Timing-Safe Array Operations
import { timingSafeIncludes } from '@bernierllc/crypto-utils';
const allowedTokens = ['token1', 'token2', 'token3'];
const isAllowed = timingSafeIncludes(allowedTokens, userToken);Complete Authentication Flow Example
import {
generateEmailMagicLink,
verifyMagicLink,
generateAccessToken,
generateRefreshToken,
verifyJWT
} from '@bernierllc/crypto-utils';
const JWT_SECRET = process.env.JWT_SECRET!;
const MAGIC_SECRET = process.env.MAGIC_SECRET!;
// Step 1: Send magic link via email
async function sendMagicLink(email: string) {
const user = await getUserByEmail(email);
if (!user) throw new Error('User not found');
const magicLink = generateEmailMagicLink(user.id, email, MAGIC_SECRET, {
expirationMinutes: 10
});
const url = `https://app.com/auth/verify?token=${magicLink.token}`;
await sendEmail(email, 'Login to Your Account', `Click: ${url}`);
}
// Step 2: Verify magic link and issue tokens
async function verifyMagicLinkAndIssueTokens(token: string) {
const verification = verifyMagicLink(token, MAGIC_SECRET);
if (!verification.valid) {
throw new Error(`Invalid magic link: ${verification.error}`);
}
const userId = verification.payload!.userId;
const user = await getUserById(userId);
const accessToken = generateAccessToken({
sub: userId,
email: user.email,
role: user.role
}, JWT_SECRET);
const refreshToken = generateRefreshToken({
sub: userId
}, JWT_SECRET);
return { accessToken, refreshToken, user };
}
// Step 3: Validate API requests
async function authenticateRequest(authHeader: string) {
const token = authHeader.replace('Bearer ', '');
const verification = verifyJWT(token, JWT_SECRET);
if (!verification.valid) {
throw new Error(`Authentication failed: ${verification.error}`);
}
return verification.payload;
}Security Best Practices
Key Generation
- Minimum 128 bits of entropy for production keys
- Use
generateRandomKey()for maximum entropy - Implement collision checking with
checkUnique
JWT Security
- Use strong secrets (256+ bits of entropy)
- Short expiration times for access tokens (15-60 minutes)
- Validate all claims (exp, iss, aud, etc.)
- Use HTTPS only for token transmission
Magic Link Security
- Short expiration times (5-15 minutes for login, 30 minutes for password reset)
- Single use only - invalidate after verification
- Rate limiting - prevent magic link spam
- HTTPS required - never send over HTTP
Constant-Time Operations
- Always use for passwords and sensitive tokens
- Use for session validation and API key checking
- Avoid string length leaks in error messages
General Security
- Never log secrets or tokens in production
- Use environment variables for secrets
- Implement proper key rotation policies
- Use HTTPS everywhere for token transmission
Error Handling
import { generateJWT, verifyJWT, generateMagicLink } from '@bernierllc/crypto-utils';
try {
// Token generation
const token = generateJWT(payload, secret);
// Token verification
const result = verifyJWT(token, secret);
if (!result.valid) {
// Handle specific errors
if (result.error?.includes('expired')) {
console.log('Token expired, refresh required');
} else if (result.error?.includes('signature')) {
console.log('Invalid token signature');
}
}
// Magic link generation
const magicLink = generateMagicLink(userId, email, secret, {
expirationMinutes: 5
});
} catch (error) {
console.error('Crypto operation failed:', error.message);
}TypeScript Support
Full TypeScript support with comprehensive type definitions:
import type {
// Key generation types
KeyOptions,
GenerationResult,
ValidationResult,
KeyEncoding,
KeyAlgorithm,
// JWT types
JWTPayload,
JWTOptions,
JWTVerifyResult,
// Magic link types
MagicLinkOptions,
MagicLinkResult,
MagicLinkVerifyResult
} from '@bernierllc/crypto-utils';
// Strongly typed JWT payload
const payload: JWTPayload = {
sub: 'user123',
email: '[email protected]',
exp: Math.floor(Date.now() / 1000) + 3600
};
// Strongly typed options
const keyOptions: KeyOptions = {
length: 32,
encoding: 'hex',
validateEntropy: true
};Performance
- High throughput: >10,000 operations/second
- Efficient encoding: Optimized implementations
- Minimal dependencies: Zero runtime dependencies
- Memory efficient: Streaming operations where possible
Migration from @bernierllc/api-key-generator
The package maintains full backward compatibility:
// All existing imports continue to work
import {
generateApiKey,
generateSecretKey,
generateRandomKey,
validateKeyFormat
} from '@bernierllc/crypto-utils'; // was @bernierllc/api-key-generatorLicense
MIT License - Copyright (c) 2025 Bernier LLC
See Also
- @bernierllc/logger - Structured logging (never logs secrets)
- @bernierllc/neverhub-adapter - Service discovery integration
