secure-node-auth
v1.4.3
Published
Blazing fast, zero-config authentication system with JWT for MySQL and PostgreSQL. Set up secure auth in seconds with auto-schema setup and extensible custom fields.
Maintainers
Readme
🔐 Secure Node Auth
Blazing fast, zero-config authentication system with JWT for MySQL & PostgreSQL. Set up secure auth in seconds!
✨ Features
- ⚡ Zero Configuration - Works out of the box with sensible defaults
- 🗄️ MySQL & PostgreSQL Support - Choose your preferred database with one line of config
- 🔒 Production-Ready Security - Bcrypt hashing, JWT tokens, rate limiting, account lockout
- 📧 Email Verification - URL-based or 6-digit code verification (new!)
- 🔍 Audit Logging - Optional security event logging (customizable)
- 🚀 Lightning Fast - Built with connection pooling and optimized queries
- 🎨 Highly Customizable - Add custom fields, hooks, and configurations
- 🔄 Auto Schema Setup - Automatically creates tables, indexes, and relationships
- 🎯 Express & Fastify Support - Pre-built routes and middleware for both frameworks
- 📦 Refresh Tokens - Secure token rotation with blacklisting
- 🛡️ Attack Protection - Rate limiting, SQL injection protection, brute force prevention
- 🔌 Extensible - Plugin system with before/after hooks
- 📝 TypeScript Support - Full type definitions included
📦 Installation
npm install secure-node-authDependencies:
mysql2orpg- Database client (MySQL or PostgreSQL)jsonwebtoken- JWT token generationbcrypt- Password hashingvalidator- Input validationexpressorfastify- Web framework (peer dependencies)
For MySQL:
npm install secure-node-auth mysql2For PostgreSQL:
npm install secure-node-auth pg🚀 Quick Start
MySQL Setup (3 lines!)
const SecureNodeAuth = require('secure-node-auth');
const auth = new SecureNodeAuth({
connection: {
type: 'mysql', // Optional, defaults to MySQL
host: 'localhost',
user: 'root',
password: 'your_password',
database: 'myapp',
},
});
await auth.init(); // Auto-creates tables!
// Use with Express
app.use('/auth', auth.router());PostgreSQL Setup (3 lines!)
const SecureNodeAuth = require('secure-node-auth');
const auth = new SecureNodeAuth({
connection: {
type: 'postgres', // Use PostgreSQL
host: 'localhost',
user: 'postgres',
password: 'your_password',
database: 'myapp',
},
});
await auth.init(); // Auto-creates tables!
// Use with Express
app.use('/auth', auth.router());📘 Complete PostgreSQL Guide - Migration, Docker setup, and advanced features.
That's it! You now have a complete authentication system with:
- ✅ User registration
- ✅ Login with JWT
- ✅ Token refresh
- ✅ Password change
- ✅ Profile management
- ✅ Logout (single/all devices)
📖 Usage Examples
Complete Express Server
require('dotenv').config();
const express = require('express');
const SecureNodeAuth = require('secure-node-auth');
const app = express();
app.use(express.json());
// Initialize auth system
const auth = new SecureNodeAuth({
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
});
await auth.init();
// Mount auth routes
app.use('/auth', auth.router());
// Protected route example
app.get('/api/profile', auth.middleware(), async (req, res) => {
const user = await auth.getUserById(req.user.userId);
res.json({ user });
});
app.listen(3000, () => console.log('Server running on port 3000'));Complete Fastify Server
const fastify = require('fastify')({ logger: true });
const SecureNodeAuth = require('secure-node-auth');
const secureNodeAuthPlugin = require('secure-node-auth/src/middleware/FastifyPlugin');
async function start() {
// Initialize auth system
const auth = new SecureNodeAuth({
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
});
await auth.init();
// Register auth plugin (includes all routes + middleware)
await fastify.register(secureNodeAuthPlugin, {
authInstance: auth,
routeOptions: {
prefix: '/auth',
},
});
// Protected route example
fastify.get(
'/api/profile',
{
preHandler: fastify.authenticate,
},
async (request, reply) => {
const user = await auth.getUserById(request.user.userId);
return { user };
}
);
await fastify.listen({ port: 3000 });
console.log('🚀 Server running on http://localhost:3000');
}
start();📘 Complete Fastify Guide - Detailed Fastify integration, rate limiting, validation, and performance tips.
Adding Custom Fields
✅ Recommended: Add fields before initialization
const auth = new SecureNodeAuth({
/* config */
});
// Add custom fields BEFORE init()
auth.addField({
name: 'phoneNumber',
type: 'VARCHAR(20)',
required: false,
unique: true,
});
auth.addField({
name: 'companyName',
type: 'VARCHAR(255)',
required: false,
});
auth.addField({
name: 'subscriptionTier',
type: "ENUM('free', 'premium', 'enterprise')",
defaultValue: 'free',
});
await auth.init();
// Now register with custom fields
await auth.register({
email: '[email protected]',
password: 'SecurePass123!',
phoneNumber: '+1234567890',
companyName: 'Tech Corp',
subscriptionTier: 'premium',
});⚠️ Advanced: Runtime schema changes (use with caution)
For existing databases, you can add columns after initialization using dangerous methods:
// Already initialized
await auth.init();
// Add single column (⚠️ can cause table locks)
await auth.dangerouslyAddColumn(
{
name: 'phoneNumber',
type: 'VARCHAR(20)',
unique: true,
},
{ confirmed: true }
);
// Add multiple columns with transaction (PostgreSQL)
await auth.dangerouslyMigrateSchema(
[
{ name: 'age', type: 'INTEGER', defaultValue: 0 },
{ name: 'city', type: 'VARCHAR(100)' },
],
{ confirmed: true, useTransaction: true }
);📖 Complete Migration Guide - Safety tips, examples, and best practices.
Using Hooks
// Before/after registration
auth.on('beforeRegister', async (userData) => {
console.log('New user signing up:', userData.email);
// Add custom validation, check blacklist, etc.
});
auth.on('afterRegister', async (result) => {
console.log('User registered:', result.user.email);
// Send welcome email, create user profile, etc.
await sendWelcomeEmail(result.user.email);
});
// Before/after login
auth.on('beforeLogin', async ({ email }) => {
console.log('Login attempt:', email);
});
auth.on('afterLogin', async (result) => {
console.log('Successful login:', result.user.email);
// Track analytics, update last login, etc.
});Manual Authentication Flow
// Register
const { user, tokens } = await auth.register({
email: '[email protected]',
password: 'SecurePass123!',
firstName: 'John',
lastName: 'Doe',
});
// Login
const login = await auth.login('[email protected]', 'SecurePass123!');
console.log(login.tokens.accessToken);
console.log(login.tokens.refreshToken);
// Verify token
const decoded = await auth.verifyAccessToken(login.tokens.accessToken);
console.log(decoded.userId, decoded.email);
// Refresh access token
const { accessToken } = await auth.refreshToken(login.tokens.refreshToken);
// Change password
await auth.changePassword(userId, 'OldPass123!', 'NewPass456!');
// Logout
await auth.logout(refreshToken);
// Logout all devices
await auth.logoutAll(userId);🔧 Configuration Options
const auth = new SecureNodeAuth({
// Database connection
connection: {
host: 'localhost',
user: 'root',
password: '',
database: 'myapp',
port: 3306,
connectionLimit: 10,
},
// JWT configuration
jwt: {
accessSecret: 'your-access-secret',
refreshSecret: 'your-refresh-secret',
accessExpiresIn: '15m', // 15 minutes
refreshExpiresIn: '7d', // 7 days
},
// Security settings
security: {
bcryptRounds: 10, // Higher = more secure, slower
maxLoginAttempts: 5, // Account lockout threshold
lockoutTime: 900000, // 15 minutes in milliseconds
passwordMinLength: 8,
passwordRequireUppercase: true,
passwordRequireNumbers: true,
passwordRequireSpecialChars: true,
},
// Custom table names
tables: {
users: 'my_users',
refreshTokens: 'my_tokens',
loginAttempts: 'my_login_attempts',
},
});📡 API Endpoints
When you mount auth.router(), these endpoints are automatically available:
| Method | Endpoint | Description | Auth Required |
| ------ | ------------------ | --------------------- | ------------- |
| POST | /register | Register new user | ❌ |
| POST | /login | Login user | ❌ |
| POST | /refresh | Refresh access token | ❌ |
| POST | /logout | Logout (revoke token) | ❌ |
| POST | /logout-all | Logout all devices | ✅ |
| GET | /me | Get current user | ✅ |
| PATCH | /me | Update user profile | ✅ |
| POST | /change-password | Change password | ✅ |
| POST | /verify | Verify token | ❌ |
| GET | /health | Health check | ❌ |
Request/Response Examples
Register:
POST /auth/register
{
"email": "[email protected]",
"password": "SecurePass123!",
"firstName": "John",
"lastName": "Doe"
}
// Response
{
"success": true,
"message": "User registered successfully",
"data": {
"user": { ... },
"tokens": {
"accessToken": "eyJhbG...",
"refreshToken": "eyJhbG...",
"expiresIn": "15m"
}
}
}Login:
POST /auth/login
{
"email": "[email protected]",
"password": "SecurePass123!"
}
// Response
{
"success": true,
"message": "Login successful",
"data": {
"user": { ... },
"tokens": { ... }
}
}📋 Complete API Methods
Core Authentication
await auth.init()- Initialize system and create tablesawait auth.register(userData)- Register new userawait auth.login(email, password)- Login and get tokensawait auth.refreshToken(refreshToken)- Get new access tokenawait auth.logout(refreshToken)- Logout single sessionawait auth.logoutAll(userId)- Logout all sessionsawait auth.verifyAccessToken(token)- Verify JWT token
User Management
await auth.getUserById(userId)- Get user by IDawait auth.getUserByEmail(email)- Get user by emailawait auth.updateUser(userId, updates)- Update user dataawait auth.updateProfile(userId, updates)- Update profile (alias)await auth.changePassword(userId, oldPass, newPass)- Change passwordawait auth.getUserCount()- Get total user countawait auth.isAccountLocked(email)- Check if account locked
Email Verification
URL-Based Verification:
await auth.sendVerificationEmail(email, url)- Send verification emailawait auth.verifyEmail(token)- Verify email with tokenawait auth.resendVerificationEmail(email, url)- Resend verification
6-Digit Code Verification (New!):
await auth.sendVerificationCode(email, options)- Send 6-digit codeawait auth.verifyCode(email, code)- Verify with 6-digit code
Utility:
await auth.isEmailVerified(userId)- Check if email verified
💡 Tip: Use URL verification for web apps, 6-digit codes for mobile apps or better UX. See Verification Guide
Password Reset
URL-Based Reset:
await auth.sendPasswordResetEmail(email, url)- Send reset emailawait auth.resetPassword(token, newPassword)- Reset password
6-Digit Code Reset (New!):
await auth.sendPasswordResetCode(email, options)- Send 6-digit reset codeawait auth.resetPasswordWithCode(email, code, newPassword)- Reset with code
💡 Tip: Use URL reset for web apps, 6-digit codes for mobile apps. Codes expire in 15 minutes (customizable).
Database Maintenance
await auth.cleanupExpiredTokens()- Clean expired tokensawait auth.cleanupExpiredLoginAttempts(days)- Clean old attemptsawait auth.cleanupRevokedRefreshTokens(days)- Clean revoked tokensawait auth.performMaintenance(options)- Run all cleanupauth.getPool()- Get raw database pool
Schema Customization
auth.addField(config)- Add field before initawait auth.dangerouslyAddColumn(config, options)- Add column at runtimeawait auth.dangerouslyMigrateSchema(fields, options)- Batch migration
Hooks & Integration
auth.on(event, callback)- Register lifecycle hooksauth.router(options)- Get Express/Fastify routerauth.middleware()- Get auth middlewareawait auth.close()- Close database connection
📖 Complete API Reference - Detailed documentation with examples
🛡️ Security Features
Password Security
- ✅ Bcrypt hashing (configurable rounds)
- ✅ Minimum length requirements
- ✅ Complexity requirements (uppercase, numbers, special chars)
- ✅ Common password blacklist
Account Protection
- ✅ Brute force protection (rate limiting)
- ✅ Account lockout after failed attempts
- ✅ Automatic lockout expiration
- ✅ Login attempt tracking
Token Security
- ✅ Separate access and refresh tokens
- ✅ Short-lived access tokens (15m default)
- ✅ Token revocation support
- ✅ Logout from all devices
- ✅ Automatic token cleanup
Database Security
- ✅ Parameterized queries (SQL injection protection)
- ✅ Connection pooling
- ✅ Indexed queries for performance
- ✅ Foreign key constraints
🏗️ Database Schema
The package automatically creates these tables:
secure_auth_users
- id (PRIMARY KEY)
- email (UNIQUE, INDEXED)
- password (HASHED)
- firstName
- lastName
- emailVerified
- isActive
- createdAt
- updatedAt
+ your custom fieldssecure_auth_refresh_tokens
- id (PRIMARY KEY)
- userId (FOREIGN KEY)
- token
- revoked
- expiresAt (INDEXED)
- createdAtsecure_auth_login_attempts
- id (PRIMARY KEY)
- email (INDEXED)
- success
- ipAddress
- userAgent
- attemptedAt🎣 Hooks System
Available hooks:
beforeRegister- Before user registrationafterRegister- After successful registrationbeforeLogin- Before login attemptafterLogin- After successful loginbeforeTokenRefresh- Before token refreshafterTokenRefresh- After token refresh
auth.on('afterRegister', async (result) => {
// Your custom logic
await sendWelcomeEmail(result.user.email);
await createUserProfile(result.user.id);
});🔌 Middleware Usage
// Protect single route
app.get('/api/protected', auth.middleware(), (req, res) => {
console.log(req.user); // { userId, email, iat, exp }
res.json({ message: 'Protected data' });
});
// Protect multiple routes
app.use('/api', auth.middleware());
// Custom role-based middleware
const requireAdmin = async (req, res, next) => {
const user = await auth.getUserById(req.user.userId);
if (user.role !== 'admin') {
return res.status(403).json({ error: 'Admin access required' });
}
next();
};
app.get('/api/admin', auth.middleware(), requireAdmin, (req, res) => {
res.json({ message: 'Admin only' });
});🧪 Testing
# Install dependencies
npm install
# Run example server
npm run example
# The server will start on http://localhost:3000Test endpoints with curl:
# Register
curl -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"SecurePass123!"}'
# Login
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"SecurePass123!"}'📚 Advanced Topics
Environment Variables
Create a .env file:
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=your_password
DB_NAME=myapp
DB_PORT=3306
JWT_ACCESS_SECRET=your_super_secret_access_key
JWT_REFRESH_SECRET=your_super_secret_refresh_key
JWT_ACCESS_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=7d
BCRYPT_ROUNDS=10
MAX_LOGIN_ATTEMPTS=5
LOCKOUT_TIME=900000
PORT=3000Token Cleanup
// Clean up expired tokens (run periodically)
await auth.db.cleanupExpiredTokens(auth.options.tables.refreshTokens);
// With cron job
const cron = require('node-cron');
cron.schedule('0 0 * * *', async () => {
await auth.db.cleanupExpiredTokens(auth.options.tables.refreshTokens);
console.log('Cleaned up expired tokens');
});Direct Database Access
// Get raw database pool for custom queries
const pool = auth.getPool();
const [rows] = await pool.execute(
'SELECT COUNT(*) as total FROM secure_auth_users WHERE isActive = ?',
[true]
);
console.log('Active users:', rows[0].total);🔧 Troubleshooting
Connection Timeout (ETIMEDOUT)
If you're getting ETIMEDOUT errors:
const auth = new SecureNodeAuth({
connection: {
host: 'localhost',
user: 'root',
password: 'password',
database: 'myapp',
// Add these timeout settings
connectTimeout: 20000, // 20 seconds (default: 10s)
acquireTimeout: 20000, // 20 seconds (default: 10s)
timeout: 20000, // 20 seconds (default: 10s)
retryAttempts: 5, // Retry 5 times (default: 3)
retryDelay: 2000 // Wait 2s between retries (default: 1s)
}
});Common causes:
- ✅ MySQL server not running →
sudo service mysql start - ✅ Wrong host/port → Verify connection details
- ✅ Firewall blocking connection → Check firewall rules
- ✅ MySQL not accepting remote connections → Edit
my.cnf(bind-address)
Connection Refused (ECONNREFUSED)
# Check if MySQL is running
sudo service mysql status
# Start MySQL
sudo service mysql start
# Check MySQL port
netstat -an | grep 3306Access Denied (ER_ACCESS_DENIED_ERROR)
-- Grant permissions to user
GRANT ALL PRIVILEGES ON myapp.* TO 'myuser'@'localhost';
FLUSH PRIVILEGES;Slow Initialization
Enable connection pooling (already enabled by default):
const auth = new SecureNodeAuth({
connection: {
connectionLimit: 20, // Max connections (default: 10)
queueLimit: 0, // No queue limit (default: 0)
enableKeepAlive: true, // Keep connections alive (default: true)
keepAliveInitialDelay: 0 // Initial delay (default: 0)
}
});📚 Additional Guides
- 🗄️ PostgreSQL Integration Guide - Complete guide for PostgreSQL, migration, and Docker setup
- ⚡ Fastify Integration Guide - Complete guide for using with Fastify framework
- 📖 Accessing User-Specific Data - Complete guide on protecting routes and accessing user's posts, orders, etc.
- ⚠️ Dangerous Migrations Guide - Runtime schema changes for existing databases (use with caution)
- 🔄 Authentication Flow Diagram - Visual guide showing how authentication works
- ⚡ Quick Reference - Common patterns cheat sheet
- 🚀 Getting Started - Step-by-step setup guide
- 🔒 Security Documentation - Comprehensive security features and best practices
- 📋 Security Audit Report - Expert-level security audit results (49 issues resolved)
🔐 Security Features
This package has undergone 5 rounds of comprehensive security audits and implements industry best practices:
- ✅ SQL Injection Protection: Parameterized queries + escaped identifiers
- ✅ Token Hashing: SHA-256 hashing of refresh tokens in database
- ✅ Email Normalization: Prevents duplicate accounts via case variations
- ✅ Audit Logging: Configurable logging for all security events
- ✅ Brute Force Protection: Account lockout after failed attempts
- ✅ Password Security: Bcrypt with configurable rounds (4-31)
- ✅ Input Validation: Multi-layer validation and sanitization
- ✅ Secure Configuration: Validated defaults with safe fallbacks
Security Standards Compliance: OWASP Top 10, NIST Framework, PCI-DSS, GDPR
See SECURITY.md for complete security documentation.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License
MIT © 2025
🔗 Links
💡 Support
If you find this package helpful, please consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs
- 💡 Suggesting new features
- 📖 Improving documentation
Built with ❤️ for the Node.js community
