@latanda/auth-middleware
v1.0.4
Published
Production-ready JWT authentication middleware for Node.js + PostgreSQL + Nginx. Battle-tested with 30+ users at latanda.online
Downloads
61
Maintainers
Readme
@latanda/auth-middleware
Production-ready JWT authentication middleware for Node.js + PostgreSQL + Nginx
Battle-tested with 30+ users at latanda.online ✨
Migration from @perks/auth-middleware
This package was previously published as @perks/auth-middleware. The scope was renamed to @latanda in v1.0.1 to align with the La Tanda project brand. The API is unchanged — only the package name and import path differ. See MIGRATION.md for full details.
npm uninstall @perks/auth-middleware && npm install @latanda/auth-middleware// Before: const auth = require('@perks/auth-middleware');
// After: const auth = require('@latanda/auth-middleware');Features
- ✅ JWT Token Generation & Validation - Secure HS256 tokens with comprehensive claim validation
- ✅ Role-Based Access Control (RBAC) - Pre-configured roles: ADMIN, MIT, IT, USER
- ✅ Express Middleware - Drop-in authentication for Express.js apps
- ✅ PostgreSQL Integration - Production-ready database schema included
- ✅ Nginx Compatible - Works seamlessly with Nginx reverse proxy
- ✅ Token Refresh - Automatic token refresh with expiration detection
- ✅ Permission System - Granular permission checks beyond roles
- ✅ Resource Ownership - Enforce users can only access their own resources
- ✅ Audit Logging - Track authentication events for security monitoring
- ✅ Zero Dependencies - Only requires
jsonwebtoken,bcrypt, andpg
Quick Start (5 minutes to secure auth)
Installation
npm install @latanda/auth-middleware jsonwebtoken bcrypt pg1. Set up PostgreSQL
# Run the schema migration
psql -U your_user -d your_database -f node_modules/@latanda/auth-middleware/sql/schema.sql2. Add to your Express app
const express = require('express');
const { createAuthMiddleware, requireRole } = require('@latanda/auth-middleware');
const app = express();
// Protect all API routes
app.use('/api/*', createAuthMiddleware({
jwtSecret: process.env.JWT_SECRET // Store this in .env file!
}));
// Require ADMIN role for admin routes
app.use('/api/admin/*', requireRole('ADMIN'));
// Your protected routes
app.get('/api/profile', (req, res) => {
// req.user is automatically populated with authenticated user
res.json({
success: true,
user: req.user // { id, email, role, permissions }
});
});
app.listen(3000, () => console.log('Secure API running on port 3000'));3. Generate tokens on login
const { generateToken } = require('@latanda/auth-middleware');
const bcrypt = require('bcrypt');
app.post('/auth/login', async (req, res) => {
const { email, password } = req.body;
// Get user from database
const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
if (!user || !await bcrypt.compare(password, user.password_hash)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Generate JWT token
const token = generateToken(user, process.env.JWT_SECRET);
res.json({
success: true,
token,
user: { id: user.id, email: user.email, role: user.role }
});
});That's it! Your API is now secured with production-ready JWT authentication.
Case Study: La Tanda
La Tanda is a Web3 tanda (rotating savings and credit association) platform serving 30+ users across 16+ groups with real financial transactions.
What we use this middleware for:
- ✅ Secure user authentication - JWT tokens with 8-hour expiration
- ✅ Role-based access - ADMIN (platform management), MIT (group coordinators), USER (members)
- ✅ API protection - All 85+ API endpoints secured
- ✅ Transaction security - Ensure users can only access their own transactions
- ✅ Admin panel - Restricted to ADMIN role with full audit logging
Results:
- 🔒 Zero authentication-related security incidents
- ⚡ Sub-100ms JWT validation performance
- 📊 Complete audit trail of all auth events
- 🚀 Seamless integration with Nginx reverse proxy
Full Documentation
API Reference
JWT Functions
const { generateToken, validateToken, refreshToken } = require('@latanda/auth-middleware');
// Generate a token
const token = generateToken(user, jwtSecret, {
expiresIn: '8h', // Default: 8 hours
issuer: 'your-app.com',
audience: 'your-app'
});
// Validate a token
const validation = validateToken(token, jwtSecret);
if (validation.valid) {
console.log('User:', validation.user_id, validation.email, validation.role);
} else {
console.error('Invalid token:', validation.error);
}
// Refresh a token (if expiring soon)
const refreshResult = refreshToken(oldToken, jwtSecret);
if (refreshResult.success) {
console.log('New token:', refreshResult.token);
}RBAC Functions
const { hasPermission, hasRoleLevel, ROLES } = require('@latanda/auth-middleware');
// Check if user has permission
if (hasPermission('ADMIN', 'user_management')) {
// User has permission
}
// Check if role meets minimum level
if (hasRoleLevel('MIT', 'USER')) { // true - MIT is higher than USER
// Access granted
}
// View all roles and permissions
console.log(ROLES);
/*
{
ADMIN: { level: 100, permissions: [...] },
MIT: { level: 50, permissions: [...] },
IT: { level: 75, permissions: [...] },
USER: { level: 10, permissions: [...] }
}
*/Middleware
const {
createAuthMiddleware,
requirePermission,
requireRole,
requireOwnership,
optionalAuth
} = require('@latanda/auth-middleware');
// Basic authentication (required)
app.use('/api/*', createAuthMiddleware({ jwtSecret: process.env.JWT_SECRET }));
// Require specific permission
app.post('/api/users', requirePermission('user_management'), (req, res) => {
// Only users with 'user_management' permission can access
});
// Require specific role or higher
app.use('/api/admin/*', requireRole('ADMIN'));
app.use('/api/groups/create', requireRole('MIT')); // MIT or ADMIN
// Enforce resource ownership
app.get('/api/transactions/:userId', requireOwnership(req => req.params.userId), (req, res) => {
// Users can only view their own transactions (unless ADMIN)
});
// Optional authentication (works with or without token)
app.get('/api/public/stats', optionalAuth({ jwtSecret: process.env.JWT_SECRET }), (req, res) => {
if (req.user) {
// Show personalized stats
} else {
// Show public stats
}
});Roles and Permissions
Pre-configured Roles
| Role | Level | Permissions |
|------|-------|-------------|
| ADMIN | 100 | full_access, user_management, system_config, approve_deposits, manage_groups, view_analytics, manage_roles, delete_users, system_settings |
| IT | 75 | view_system_logs, debug_access, api_access, view_analytics, technical_support |
| MIT | 50 | create_groups, manage_own_groups, approve_members, view_group_analytics, edit_group_settings |
| USER | 10 | view_own_profile, edit_own_profile, join_groups, make_payments, view_own_transactions |
Custom Permissions
You can add custom permissions to the database:
INSERT INTO user_permissions (user_id, permission, granted_by)
VALUES (123, 'beta_access', 1);Then check in your code:
if (req.user.permissions.includes('beta_access')) {
// Show beta features
}Database Schema
The included PostgreSQL schema provides:
- ✅ users table - User accounts with bcrypt password hashes
- ✅ sessions table - Active JWT token tracking (optional, for revocation)
- ✅ user_permissions table - Custom per-user permissions
- ✅ auth_audit_log table - Authentication event audit trail
See sql/schema.sql for full details.
Nginx Integration
This middleware works seamlessly with Nginx reverse proxy:
# /etc/nginx/sites-available/your-app
location /api/ {
proxy_pass http://localhost:3000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# CORS headers for JWT
add_header Access-Control-Allow-Origin * always;
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS" always;
add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
if ($request_method = OPTIONS) {
return 204;
}
}Then your frontend can make authenticated requests:
fetch('https://your-app.com/api/profile', {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
})Advanced Usage
Custom Unauthorized Handler
app.use('/api/*', createAuthMiddleware({
jwtSecret: process.env.JWT_SECRET,
onUnauthorized: (req, res, error) => {
// Custom error handling
console.error('Auth failed:', error);
res.status(401).json({
success: false,
error: 'Custom error message',
code: error.code
});
}
}));Token Refresh Strategy
const { isTokenExpiringSoon, refreshToken } = require('@latanda/auth-middleware');
app.post('/api/refresh-token', (req, res) => {
const token = req.headers.authorization?.substring(7);
if (isTokenExpiringSoon(token, 15)) { // Refresh if expires within 15 min
const result = refreshToken(token, process.env.JWT_SECRET);
if (result.success) {
return res.json({ success: true, token: result.token });
}
}
res.json({ success: false, error: 'Token not eligible for refresh' });
});Group Ownership Example (La Tanda use case)
const { canPerformGroupAction } = require('@latanda/auth-middleware');
app.post('/api/groups/:groupId/approve-member', async (req, res) => {
const group = await db.query('SELECT * FROM groups WHERE id = $1', [req.params.groupId]);
// Check if user can approve members in this group
if (!canPerformGroupAction(req.user.id, group, req.user.role, 'approve_members')) {
return res.status(403).json({ error: 'Not authorized to approve members' });
}
// Approve member...
});Examples
See the examples/ directory for complete working examples:
- basic-usage.js - Simple Express app with JWT auth
- role-based-routing.js - Different routes for different roles
- refresh-token-flow.js - Automatic token refresh implementation
- nginx-integration/ - Complete Nginx + Express + PostgreSQL setup
Testing
npm testSecurity Considerations
🔒 Always use HTTPS in production - JWT tokens are bearer tokens and should be transmitted securely
🔒 Store JWT_SECRET securely - Use environment variables, never commit secrets
🔒 Set appropriate token expiration - Balance security (shorter) vs user experience (longer)
🔒 Implement token refresh - Allow users to stay logged in without re-authentication
🔒 Monitor audit logs - Watch for suspicious authentication patterns
🔒 Use bcrypt for passwords - Never store plain-text passwords
💰 Support This Project
If this package saves you time and helps secure your application, consider supporting its development!
Why support?
- ✅ Ongoing maintenance and security updates
- ✅ New features based on community feedback
- ✅ Comprehensive documentation and examples
- ✅ Priority support for sponsors
Other ways to help:
- ⭐ Star this repo on GitHub
- 🐛 Report bugs and suggest features
- 📝 Improve documentation
- 🔀 Submit pull requests
💼 Need Help?
Professional services available:
- 📧 Implementation consulting
- 🔧 Custom feature development
- 🏢 Enterprise support contracts
Contact: [email protected]
Contributing
Contributions welcome! See CONTRIBUTING.md
License
MIT © Narjell Ebanks
Support
Acknowledgments
Extracted from the La Tanda Web3 platform - a production tanda (ROSCA) system serving real users with real money.
Special thanks to the La Tanda community for battle-testing this authentication system!
Built with ❤️ by the La Tanda team
