Authentication & Security
Authentication & Security
Section titled “Authentication & Security”📖 Introduction
Section titled “📖 Introduction”Authentication and security are non-negotiable in production APIs. This covers everything from password hashing and JWT tokens to CORS configuration and rate limiting — the tools that keep your application and users safe.
Security isn’t a feature. It’s a requirement.
📊 Mermaid Diagram 1: JWT Auth Flow
Section titled “📊 Mermaid Diagram 1: JWT Auth Flow”sequenceDiagram participant Client participant Server participant DB
Client->>Server: POST /login { email, password } Server->>DB: Find user by email DB-->>Server: User found Server->>Server: bcrypt.compare(password, hash) alt Valid password Server->>Server: Generate JWT (sign with secret) Server-->>Client: 200 { token, user }
Client->>Server: GET /profile (Authorization: Bearer token) Server->>Server: Verify JWT signature alt Valid token Server-->>Client: 200 { user data } else Expired/invalid Server-->>Client: 401 Unauthorized end else Invalid password Server-->>Client: 401 { error: 'Invalid credentials' } end🏗️ Architecture: Security Layers
Section titled “🏗️ Architecture: Security Layers”flowchart TB subgraph Layer1["Layer 1: Transport"] L1a["HTTPS/TLS"] L1b["HSTS Header"] end subgraph Layer2["Layer 2: Application"] L2a["helmet() headers"] L2b["CORS config"] L2c["Rate limiting"] end subgraph Layer3["Layer 3: Authentication"] L3a["JWT / Sessions"] L3b["Password hashing"] L3c["MFA / OAuth"] end subgraph Layer4["Layer 4: Input"] L4a["Input validation"] L4b["SQL injection prevention"] L4c["XSS sanitization"] end
Layer1 --> Layer2 --> Layer3 --> Layer4📝 Syntax
Section titled “📝 Syntax”// JWTconst jwt = require('jsonwebtoken');const token = jwt.sign({ userId: 1, role: 'admin' }, process.env.JWT_SECRET, { expiresIn: '7d' });jwt.verify(token, process.env.JWT_SECRET);
// Bcryptconst bcrypt = require('bcrypt');const hash = await bcrypt.hash(password, 12);const match = await bcrypt.compare(password, hash);
// Helmetapp.use(require('helmet')());
// CORSapp.use(require('cors')({ origin: 'https://myapp.com' }));
// Rate limitapp.use(require('express-rate-limit')({ windowMs: 15*60*1000, max: 100 }));🟢 Basic Example: JWT Auth Middleware
Section titled “🟢 Basic Example: JWT Auth Middleware”const jwt = require('jsonwebtoken');
function authenticate(req, res, next) { const header = req.headers.authorization; if (!header) { return res.status(401).json({ error: 'No token provided' }); }
const token = header.split(' ')[1]; // Bearer <token>
try { const decoded = jwt.verify(token, process.env.JWT_SECRET); req.user = decoded; next(); } catch (err) { return res.status(401).json({ error: 'Invalid or expired token' }); }}
// Protect routesapp.get('/profile', authenticate, (req, res) => { res.json({ user: req.user });});🟡 Intermediate Example: Login + Signup with Bcrypt
Section titled “🟡 Intermediate Example: Login + Signup with Bcrypt”const bcrypt = require('bcrypt');const jwt = require('jsonwebtoken');
// Signupapp.post('/signup', async (req, res) => { const { email, password } = req.body;
const existing = await User.findOne({ email }); if (existing) return res.status(409).json({ error: 'Email already exists' });
const hashed = await bcrypt.hash(password, 12); const user = await User.create({ email, password: hashed });
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.status(201).json({ token, user: { id: user.id, email: user.email } });});
// Loginapp.post('/login', async (req, res) => { const { email, password } = req.body;
const user = await User.findOne({ email }); if (!user) return res.status(401).json({ error: 'Invalid credentials' });
const match = await bcrypt.compare(password, user.password); if (!match) return res.status(401).json({ error: 'Invalid credentials' });
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '7d' }); res.json({ token, user: { id: user.id, email: user.email } });});🔴 Advanced Example: Role-Based Access Control (RBAC)
Section titled “🔴 Advanced Example: Role-Based Access Control (RBAC)”function authorize(...allowedRoles) { return (req, res, next) => { if (!req.user) return res.status(401).json({ error: 'Auth required' }); if (!allowedRoles.includes(req.user.role)) { return res.status(403).json({ error: 'Insufficient permissions' }); } next(); };}
// Usage: only admins can delete usersapp.delete('/users/:id', authenticate, authorize('admin'), async (req, res) => { await User.findByIdAndDelete(req.params.id); res.status(204).end();});
// Moderators and admins can editapp.put('/posts/:id', authenticate, authorize('moderator', 'admin'), async (req, res) => { const post = await Post.findByIdAndUpdate(req.params.id, req.body, { new: true }); res.json(post);});🏭 Production Example: Complete Security Setup
Section titled “🏭 Production Example: Complete Security Setup”const express = require('express');const helmet = require('helmet');const cors = require('cors');const rateLimit = require('express-rate-limit');const mongoSanitize = require('express-mongo-sanitize');const hpp = require('hpp');
const app = express();
// 1. Security headersapp.use(helmet());
// 2. CORSapp.use(cors({ origin: process.env.ALLOWED_ORIGINS?.split(','), methods: ['GET', 'POST', 'PUT', 'DELETE'], allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, maxAge: 86400, // 24 hours}));
// 3. Rate limitingconst limiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 100, message: { error: 'Too many requests' },});app.use('/api', limiter);
// Stricter limiter for auth routesconst authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, message: { error: 'Too many login attempts' },});app.use('/api/auth', authLimiter);
// 4. Data sanitizationapp.use(mongoSanitize()); // Prevent NoSQL injectionapp.use(hpp()); // Prevent HTTP parameter pollution
// 5. Body size limitsapp.use(express.json({ limit: '10kb' }));
// 6. Auth middlewareapp.use('/api', authenticate);
// 7. Routesapp.use('/api/v1/users', userRoutes);📦 Performance Notes
Section titled “📦 Performance Notes”| Operation | Time | Notes |
|---|---|---|
| bcrypt.hash (salt 12) | ~250ms | CPU-intensive by design |
| JWT sign | ~0.1ms | Fast (symmetric) |
| JWT verify | ~0.1ms | Fast |
| Rate limit check | ~0.01ms | In-memory (fast) |
🔒 Security Notes
Section titled “🔒 Security Notes”- Always use HTTPS in production
- Hash passwords with bcrypt (cost 12+)
- Never store plaintext passwords
- Use short-lived JWTs (15min access + 7d refresh)
- Rate limit auth endpoints
- Validate all CORS origins
⚠️ Common Mistakes
Section titled “⚠️ Common Mistakes”// MISTAKE: Storing password in plaintextUser.create({ email, password: req.body.password }); // ❌
// MISTAKE: No rate limiting on loginapp.post('/login', login); // Anyone can brute-force
// MISTAKE: CORS set to wildcard in productionapp.use(cors()); // Allows ALL origins!🚀 Best Practices
Section titled “🚀 Best Practices”| # | Practice |
|---|---|
| 1 | Hash passwords with bcrypt (cost 12) |
| 2 | Use short-lived JWTs with refresh tokens |
| 3 | Rate limit all endpoints (stricter on auth) |
| 4 | Use helmet() for security headers |
| 5 | Never trust CORS for authentication |
📝 MCQs
Section titled “📝 MCQs”1. What algorithm should you use to hash passwords?
- A) MD5
- B) SHA256
- C) bcrypt ✅
- D) Base64
2. Where should JWT tokens be stored on the client?
- A) localStorage
- B) httpOnly cookie ✅
- C) URL parameter
- D) SessionStorage
3. What does helmet() do?
- A) Enables CORS
- B) Sets security headers ✅
- C) Rate limits requests
- D) Hashes passwords
4. What status code for unauthorized?
- A) 400
- B) 401 ✅
- C) 403
- D) 404
5. What’s the difference between 401 and 403?
- A) No difference
- B) 401=not authenticated, 403=not authorized ✅
- C) 401=bad request, 403=not found
- D) 401=expired, 403=invalid
💻 Coding Challenge 1: Auth Middleware
Section titled “💻 Coding Challenge 1: Auth Middleware”Build middleware that extracts JWT from Authorization header and attaches user to req.
💻 Coding Challenge 2: RBAC System
Section titled “💻 Coding Challenge 2: RBAC System”Implement a role-based access control system with admin, moderator, and user roles.
💻 Coding Challenge 3: Rate Limiter
Section titled “💻 Coding Challenge 3: Rate Limiter”Build an in-memory rate limiter (without third-party packages) that limits to N requests per minute per IP.
🧪 Mini Exercise
Section titled “🧪 Mini Exercise”// Find 3 security vulnerabilities:app.post('/login', async (req, res) => { const user = await User.findOne({ email: req.body.email }); if (user.password === req.body.password) { const token = jwt.sign(user, 'secret123'); res.json({ token }); }});🌍 Real World Problem
Section titled “🌍 Real World Problem”Problem: Your API is being brute-forced. Attackers are trying thousands of passwords on user accounts. Rate limiting on IP isn’t working because they rotate IPs. What’s your strategy?
🏗️ Mini Project: Auth Server
Section titled “🏗️ Mini Project: Auth Server”Build a complete auth server with: signup, login, JWT access + refresh tokens, password reset, and rate limiting.
📖 Summary
Section titled “📖 Summary”| Concept | Key |
|---|---|
| Password hashing | bcrypt with cost 12 |
| JWT | Stateless tokens with expiry |
| CORS | Whitelist allowed origins |
| Rate limiting | Prevent brute-force |
| Authorization | Role-based access control |
📋 Cheat Sheet
Section titled “📋 Cheat Sheet”const bcrypt = require('bcrypt');const jwt = require('jsonwebtoken');const hash = await bcrypt.hash(password, 12);const token = jwt.sign({ id: user.id }, SECRET, { expiresIn: '7d' });const decoded = jwt.verify(token, SECRET);app.use(require('helmet')());app.use(require('cors')({ origin: 'https://app.com' }));app.use(require('express-rate-limit')({ windowMs: 15*60*1000, max: 100 }));📚 Further Reading
Section titled “📚 Further Reading”🔗 Related Topics
Section titled “🔗 Related Topics”| Topic | Link |
|---|---|
| Input Validation | Previous |
| File Uploads | Next |
| WebSockets | WebSockets |