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.
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 / Path | Description |
|---|---|
| /src/config/env.ts | Environment configuration with Zod validation. |
| /src/models/User.ts | User schema with roles, MFA settings, and security fields. |
| /src/models/AuditLog.ts | Security audit logging for critical actions. |
| /src/services/auth.service.ts | Core authentication logic (register, login, password management). |
| /src/services/token.service.ts | JWT token generation, validation, and refresh mechanisms. |
| /src/services/mfa.service.ts | TOTP setup, verification, and QR code generation. |
| /src/services/email.service.ts | Email verification and password reset notifications. |
| /src/services/audit.service.ts | Security event logging and tracking. |
| /src/controllers/auth.controller.ts | Authentication endpoint handlers. |
| /src/controllers/user.controller.ts | User management and profile operations. |
| /src/middlewares/auth.middleware.ts | JWT token validation and user extraction. |
| /src/middlewares/rbac.middleware.ts | Role-based access control enforcement. |
| /src/middlewares/rateLimit.ts | Redis-backed rate limiting protection. |
| /src/middlewares/validate.ts | Request validation using Zod schemas. |
| /src/routes/auth.routes.ts | Complete authentication API routes with Swagger docs. |
| /src/routes/user.routes.ts | User management routes with admin controls. |
| /src/utils/logger.ts | Winston logging configuration. |
| /.env.example | All required environment variables with descriptions. |
Files to be modified
| File / Path | Description |
|---|---|
| server.ts | Database connections (MongoDB + Redis), middleware setup, route mounting. |
| app.ts | Express configuration, Swagger setup, CORS, security headers. |
| .env | Authentication secrets, database URLs, email configuration. |
| package.json | Installs 20+ production dependencies (express, mongoose, redis, jwt, argon2, etc.). |
| docker-compose.yml | Complete containerization with MongoDB and Redis services. |
| Dockerfile | Production-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.
Register a new user account with email and password.
Login with email and password to receive JWT tokens.
Refresh expired access token using refresh token.
Verify user email address using verification token.
Request password reset email with reset token.
Reset password using reset token from email.
Setup Multi-Factor Authentication with TOTP (requires login).
Verify and enable MFA setup using TOTP token.
Complete login with MFA token (after initial login).
Get current user profile information.
Update user profile information.
List all users (Admin only).
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});