Enterprise Authentication Backend

Production-ready authentication backend with JWT tokens, MFA support, role-based access control, password reset flows, audit logging, rate limiting, and comprehensive security features.

authenticationjwtmfarbacsecuritymongodbredisexpresstypescriptoauthtotpargon2
v1.0.0Authentication & Security

Overview

A production-ready authentication backend built with Express.js and TypeScript. Supports JWT access & refresh tokens with automatic rotation. TOTP-based Multi-Factor Authentication with QR code generation. Role-based access control (RBAC) with admin and user roles. Comprehensive password security with Argon2 hashing. Redis-powered rate limiting and token blacklisting. Email verification and password reset flows. Complete audit logging for security events. MongoDB integration with Mongoose ODM. OAuth2 ready (Google/GitHub integration stubs). Comprehensive Swagger API documentation. Docker containerization for easy deployment. Built with security-first approach and production scalability.

What This Does

Sets up a complete enterprise-grade authentication system with JWT tokens, MFA, RBAC, security features, and audit logging.

Files & Folders Created

File / PathDescription
/src/config/env.tsEnvironment configuration with Zod validation.
/src/models/User.tsUser schema with roles, MFA settings, and security fields.
/src/models/AuditLog.tsSecurity audit logging for critical actions.
/src/services/auth.service.tsCore authentication logic (register, login, password management).
/src/services/token.service.tsJWT token generation, validation, and refresh mechanisms.
/src/services/mfa.service.tsTOTP setup, verification, and QR code generation.
/src/services/email.service.tsEmail verification and password reset notifications.
/src/services/audit.service.tsSecurity event logging and tracking.
/src/controllers/auth.controller.tsAuthentication endpoint handlers.
/src/controllers/user.controller.tsUser management and profile operations.
/src/middlewares/auth.middleware.tsJWT token validation and user extraction.
/src/middlewares/rbac.middleware.tsRole-based access control enforcement.
/src/middlewares/rateLimit.tsRedis-backed rate limiting protection.
/src/middlewares/validate.tsRequest validation using Zod schemas.
/src/routes/auth.routes.tsComplete authentication API routes with Swagger docs.
/src/routes/user.routes.tsUser management routes with admin controls.
/src/utils/logger.tsWinston logging configuration.
/.env.exampleAll required environment variables with descriptions.

Files to be modified

File / PathDescription
server.tsDatabase connections (MongoDB + Redis), middleware setup, route mounting.
app.tsExpress configuration, Swagger setup, CORS, security headers.
.envAuthentication secrets, database URLs, email configuration.
package.jsonInstalls 20+ production dependencies (express, mongoose, redis, jwt, argon2, etc.).
docker-compose.ymlComplete containerization with MongoDB and Redis services.
DockerfileProduction-ready Docker image configuration.

Configuration

# Environment Variables Configuration

# Server Configuration
PORT=3000                        # Server port (default: 3000)
NODE_ENV=development            # Environment: development | production | test
BASE_URL=http://localhost:3000  # Application base URL for redirects

# Database Configuration
MONGO_URI=mongodb://localhost:27017/auth-db  # MongoDB connection string
REDIS_URL=redis://localhost:6379            # Redis connection string

# JWT Security Configuration
JWT_ACCESS_SECRET=your_super_secret_access_key_here    # Access token signing secret
JWT_REFRESH_SECRET=your_super_secret_refresh_key_here  # Refresh token signing secret
JWT_ACCESS_EXPIRY=15m                                  # Access token expiry (15 minutes)
JWT_REFRESH_EXPIRY=7d                                  # Refresh token expiry (7 days)

# Email Service Configuration (for verification & password reset)
SMTP_HOST=smtp.ethereal.email    # SMTP server hostname
SMTP_PORT=587                    # SMTP server port
SMTP_USER=your_smtp_username     # SMTP authentication username
SMTP_PASS=your_smtp_password     # SMTP authentication password
FROM_EMAIL=noreply@yourapp.com   # Sender email address

# Security Notes:
# - Use strong, unique secrets for JWT tokens (32+ characters)
# - Enable SSL/TLS for SMTP in production
# - Use environment-specific MongoDB/Redis instances
# - Set NODE_ENV=production for production deployments

Frontend Integration

Here's an example of integrating the authentication system with a Next.js frontend using React hooks. All protected endpoints require Bearer token authentication with JWT tokens.

POST/auth/register

Register a new user account with email and password.

POST/auth/login

Login with email and password to receive JWT tokens.

POST/auth/refresh

Refresh expired access token using refresh token.

POST/auth/verify-email

Verify user email address using verification token.

POST/auth/request-password-reset

Request password reset email with reset token.

POST/auth/reset-password

Reset password using reset token from email.

POST/auth/mfa/setup

Setup Multi-Factor Authentication with TOTP (requires login).

POST/auth/mfa/verify

Verify and enable MFA setup using TOTP token.

POST/auth/mfa/authenticate

Complete login with MFA token (after initial login).

GET/users/profile

Get current user profile information.

PUT/users/profile

Update user profile information.

GET/users

List all users (Admin only).

POST/users/{id}/roles

Assign roles to a user (Admin only).

Example

1// app/components/AuthProvider.jsx 2'use client'; 3import { createContext, useContext, useEffect, useState } from 'react'; 4 5const AuthContext = createContext(); 6const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'; 7 8export function AuthProvider({ children }) { 9 const [user, setUser] = useState(null); 10 const [loading, setLoading] = useState(true); 11 const [accessToken, setAccessToken] = useState(null); 12 13 useEffect(() => { 14 checkAuthStatus(); 15 }, []); 16 17 async function checkAuthStatus() { 18 const token = localStorage.getItem('accessToken'); 19 if (!token) { 20 setLoading(false); 21 return; 22 } 23 24 try { 25 const response = await fetch(`${API_BASE_URL}/users/profile`, { 26 headers: { Authorization: `Bearer ${token}` }, 27 }); 28 29 if (response.ok) { 30 const userData = await response.json(); 31 setUser(userData); 32 setAccessToken(token); 33 } else { 34 // Try to refresh token 35 await refreshToken(); 36 } 37 } catch (error) { 38 console.error('Auth check failed:', error); 39 localStorage.removeItem('accessToken'); 40 } finally { 41 setLoading(false); 42 } 43 } 44 45 async function refreshToken() { 46 try { 47 const response = await fetch(`${API_BASE_URL}/auth/refresh`, { 48 method: 'POST', 49 credentials: 'include', // Include cookies for refresh token 50 }); 51 52 if (response.ok) { 53 const data = await response.json(); 54 localStorage.setItem('accessToken', data.accessToken); 55 setAccessToken(data.accessToken); 56 setUser(data.user); 57 return data.accessToken; 58 } else { 59 throw new Error('Token refresh failed'); 60 } 61 } catch (error) { 62 console.error('Token refresh failed:', error); 63 localStorage.removeItem('accessToken'); 64 setUser(null); 65 setAccessToken(null); 66 return null; 67 } 68 } 69 70 async function login(email, password) { 71 try { 72 const response = await fetch(`${API_BASE_URL}/auth/login`, { 73 method: 'POST', 74 headers: { 'Content-Type': 'application/json' }, 75 credentials: 'include', 76 body: JSON.stringify({ email, password }), 77 }); 78 79 const data = await response.json(); 80 81 if (!response.ok) { 82 throw new Error(data.message || 'Login failed'); 83 } 84 85 // Check if MFA is required 86 if (data.mfaRequired) { 87 return { mfaRequired: true, tempToken: data.tempToken }; 88 } 89 90 // Regular login success 91 localStorage.setItem('accessToken', data.accessToken); 92 setAccessToken(data.accessToken); 93 setUser(data.user); 94 return { success: true, user: data.user }; 95 } catch (error) { 96 console.error('Login failed:', error); 97 throw error; 98 } 99 } 100 101 async function authenticateMFA(tempToken, mfaToken) { 102 try { 103 const response = await fetch(`${API_BASE_URL}/auth/mfa/authenticate`, { 104 method: 'POST', 105 headers: { 'Content-Type': 'application/json' }, 106 credentials: 'include', 107 body: JSON.stringify({ tempToken, token: mfaToken }), 108 }); 109 110 const data = await response.json(); 111 112 if (!response.ok) { 113 throw new Error(data.message || 'MFA authentication failed'); 114 } 115 116 localStorage.setItem('accessToken', data.accessToken); 117 setAccessToken(data.accessToken); 118 setUser(data.user); 119 return { success: true, user: data.user }; 120 } catch (error) { 121 console.error('MFA authentication failed:', error); 122 throw error; 123 } 124 } 125 126 async function register(email, password, displayName) { 127 try { 128 const response = await fetch(`${API_BASE_URL}/auth/register`, { 129 method: 'POST', 130 headers: { 'Content-Type': 'application/json' }, 131 body: JSON.stringify({ email, password, displayName }), 132 }); 133 134 const data = await response.json(); 135 136 if (!response.ok) { 137 throw new Error(data.message || 'Registration failed'); 138 } 139 140 return { success: true, message: data.message }; 141 } catch (error) { 142 console.error('Registration failed:', error); 143 throw error; 144 } 145 } 146 147 async function logout() { 148 try { 149 await fetch(`${API_BASE_URL}/auth/logout`, { 150 method: 'POST', 151 credentials: 'include', 152 }); 153 } catch (error) { 154 console.error('Logout error:', error); 155 } finally { 156 localStorage.removeItem('accessToken'); 157 setAccessToken(null); 158 setUser(null); 159 } 160 } 161 162 async function setupMFA() { 163 try { 164 const response = await fetch(`${API_BASE_URL}/auth/mfa/setup`, { 165 method: 'POST', 166 headers: { Authorization: `Bearer ${accessToken}` }, 167 }); 168 169 const data = await response.json(); 170 171 if (!response.ok) { 172 throw new Error(data.message || 'MFA setup failed'); 173 } 174 175 return data; // Returns { qrCode, secret, backupCodes } 176 } catch (error) { 177 console.error('MFA setup failed:', error); 178 throw error; 179 } 180 } 181 182 async function verifyMFA(token) { 183 try { 184 const response = await fetch(`${API_BASE_URL}/auth/mfa/verify`, { 185 method: 'POST', 186 headers: { 187 'Content-Type': 'application/json', 188 Authorization: `Bearer ${accessToken}`, 189 }, 190 body: JSON.stringify({ token }), 191 }); 192 193 const data = await response.json(); 194 195 if (!response.ok) { 196 throw new Error(data.message || 'MFA verification failed'); 197 } 198 199 // Refresh user data to reflect MFA enabled status 200 await checkAuthStatus(); 201 return { success: true }; 202 } catch (error) { 203 console.error('MFA verification failed:', error); 204 throw error; 205 } 206 } 207 208 const value = { 209 user, 210 loading, 211 accessToken, 212 login, 213 register, 214 logout, 215 setupMFA, 216 verifyMFA, 217 authenticateMFA, 218 refreshToken, 219 }; 220 221 return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>; 222} 223 224export function useAuth() { 225 const context = useContext(AuthContext); 226 if (!context) { 227 throw new Error('useAuth must be used within an AuthProvider'); 228 } 229 return context; 230} 231 232// app/components/LoginForm.jsx 233'use client'; 234import { useState } from 'react'; 235import { useAuth } from './AuthProvider'; 236 237export default function LoginForm() { 238 const { login, authenticateMFA } = useAuth(); 239 const [formData, setFormData] = useState({ email: '', password: '' }); 240 const [mfaData, setMfaData] = useState({ tempToken: '', mfaToken: '' }); 241 const [showMFA, setShowMFA] = useState(false); 242 const [loading, setLoading] = useState(false); 243 const [error, setError] = useState(''); 244 245 async function handleLogin(e) { 246 e.preventDefault(); 247 setError(''); 248 setLoading(true); 249 250 try { 251 const result = await login(formData.email, formData.password); 252 253 if (result.mfaRequired) { 254 setMfaData({ tempToken: result.tempToken, mfaToken: '' }); 255 setShowMFA(true); 256 } else { 257 // Login success 258 window.location.href = '/dashboard'; 259 } 260 } catch (err) { 261 setError(err.message); 262 } finally { 263 setLoading(false); 264 } 265 } 266 267 async function handleMFASubmit(e) { 268 e.preventDefault(); 269 setError(''); 270 setLoading(true); 271 272 try { 273 await authenticateMFA(mfaData.tempToken, mfaData.mfaToken); 274 window.location.href = '/dashboard'; 275 } catch (err) { 276 setError(err.message); 277 } finally { 278 setLoading(false); 279 } 280 } 281 282 if (showMFA) { 283 return ( 284 <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md"> 285 <h2 className="text-2xl font-bold mb-6">Two-Factor Authentication</h2> 286 <form onSubmit={handleMFASubmit} className="space-y-4"> 287 <div> 288 <label className="block text-sm font-medium mb-2"> 289 Enter 6-digit code from your authenticator app 290 </label> 291 <input 292 type="text" 293 value={mfaData.mfaToken} 294 onChange={(e) => setMfaData({ ...mfaData, mfaToken: e.target.value })} 295 placeholder="123456" 296 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500" 297 maxLength={6} 298 required 299 /> 300 </div> 301 {error && ( 302 <div className="p-3 bg-red-100 border border-red-400 text-red-700 rounded"> 303 {error} 304 </div> 305 )} 306 <button 307 type="submit" 308 disabled={loading} 309 className="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-semibold py-2 rounded-md" 310 > 311 {loading ? 'Verifying...' : 'Verify'} 312 </button> 313 </form> 314 </div> 315 ); 316 } 317 318 return ( 319 <div className="max-w-md mx-auto p-6 bg-white rounded-lg shadow-md"> 320 <h2 className="text-2xl font-bold mb-6">Login</h2> 321 <form onSubmit={handleLogin} className="space-y-4"> 322 <div> 323 <label className="block text-sm font-medium mb-2">Email</label> 324 <input 325 type="email" 326 value={formData.email} 327 onChange={(e) => setFormData({ ...formData, email: e.target.value })} 328 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500" 329 required 330 /> 331 </div> 332 <div> 333 <label className="block text-sm font-medium mb-2">Password</label> 334 <input 335 type="password" 336 value={formData.password} 337 onChange={(e) => setFormData({ ...formData, password: e.target.value })} 338 className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:border-blue-500" 339 required 340 /> 341 </div> 342 {error && ( 343 <div className="p-3 bg-red-100 border border-red-400 text-red-700 rounded"> 344 {error} 345 </div> 346 )} 347 <button 348 type="submit" 349 disabled={loading} 350 className="w-full bg-blue-500 hover:bg-blue-600 disabled:bg-gray-400 text-white font-semibold py-2 rounded-md" 351 > 352 {loading ? 'Logging in...' : 'Login'} 353 </button> 354 </form> 355 </div> 356 ); 357}

Usage

1// server.js - Main application setup 2import express from 'express'; 3import cors from 'cors'; 4import dotenv from 'dotenv'; 5import { fal } from '@fal-ai/client'; 6 7// Import routes 8import paymentRoutes from './routes/payment.routes'; 9import { router as webhookRouter } from './routes/webhook.routes'; 10import { authMiddleware } from './middleware/auth.middleware'; 11import { FalAIModel } from './models/FalAIModel'; 12import { prismaClient } from './prisma'; 13 14dotenv.config(); 15 16// Configure Fal.AI 17fal.config({ 18 credentials: process.env.FAL_KEY, 19}); 20 21const app = express(); 22const PORT = process.env.PORT || 8080; 23 24// Middleware 25app.use(cors({ 26 origin: ['http://localhost:3000', process.env.FRONTEND_URL], 27 credentials: true, 28 methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], 29 allowedHeaders: ['Content-Type', 'Authorization'], 30})); 31app.use(express.json()); 32 33// Initialize Fal.AI Model 34const falAiModel = new FalAIModel(); 35 36// Routes (Protected with authMiddleware) 37app.use('/payment', authMiddleware, paymentRoutes); 38app.use('/api/webhook', webhookRouter); // Webhooks don't need auth 39 40// Image Generation Routes 41app.post('/ai/training', authMiddleware, async (req, res) => { 42 try { 43 const { zipUrl, name, type, age, ethinicity, eyeColor, bald } = req.body; 44 45 const { request_id } = await falAiModel.trainModel(zipUrl, name); 46 47 const model = await prismaClient.model.create({ 48 data: { 49 name, 50 type, 51 age, 52 ethinicity, 53 eyeColor, 54 bald, 55 userId: req.userId!, 56 zipUrl, 57 falAiRequestId: request_id, 58 }, 59 }); 60 61 res.json({ modelId: model.id }); 62 } catch (error) { 63 console.error('Training error:', error); 64 res.status(500).json({ message: 'Training failed' }); 65 } 66}); 67 68app.post('/ai/generate', authMiddleware, async (req, res) => { 69 try { 70 const { modelId, prompt } = req.body; 71 72 const model = await prismaClient.model.findUnique({ 73 where: { id: modelId }, 74 }); 75 76 if (!model || !model.tensorPath) { 77 res.status(404).json({ message: 'Model not found' }); 78 return; 79 } 80 81 // Check credits 82 const credits = await prismaClient.userCredit.findUnique({ 83 where: { userId: req.userId! }, 84 }); 85 86 if ((credits?.amount ?? 0) < 1) { 87 res.status(402).json({ message: 'Insufficient credits' }); 88 return; 89 } 90 91 const { request_id } = await falAiModel.generateImage(prompt, model.tensorPath); 92 93 const image = await prismaClient.outputImages.create({ 94 data: { 95 prompt, 96 userId: req.userId!, 97 modelId, 98 imageUrl: '', 99 falAiRequestId: request_id, 100 }, 101 }); 102 103 // Deduct credit 104 await prismaClient.userCredit.update({ 105 where: { userId: req.userId! }, 106 data: { amount: { decrement: 1 } }, 107 }); 108 109 res.json({ imageId: image.id }); 110 } catch (error) { 111 console.error('Generation error:', error); 112 res.status(500).json({ message: 'Generation failed' }); 113 } 114}); 115 116app.get('/models', authMiddleware, async (req, res) => { 117 const models = await prismaClient.model.findMany({ 118 where: { userId: req.userId }, 119 }); 120 res.json({ models }); 121}); 122 123// Start server 124app.listen(PORT, () => { 125 console.log(`� Auth Backend running on port ${PORT}`); 126});