Episode 3 — NodeJS MongoDB Backend Architecture / 3.14 — Authentication and Authorization
3.14.a — Authentication vs Authorization
In one sentence: Authentication verifies who you are (identity); authorization determines what you can do (permissions) -- every secure backend needs both, and they happen at different stages of the request lifecycle.
Navigation: <- 3.14 Overview | 3.14.b -- Password Security with Bcrypt ->
1. The core distinction
| Authentication (AuthN) | Authorization (AuthZ) | |
|---|---|---|
| Question | "Who are you?" | "What can you do?" |
| Analogy | Showing your ID card at the door | Having a VIP pass to backstage |
| Input | Credentials (username, password, token) | Identity + resource being accessed |
| Output | Confirmed identity or rejection | Allow or deny the action |
| When | Before authorization -- always first | After authentication succeeds |
| Failure code | 401 Unauthorized (really means unauthenticated) | 403 Forbidden |
2. Real-world analogy: the airport
| Step | Airport | Web App |
|---|---|---|
| Authentication | Show passport at check-in -- prove you are John Doe | POST /login with email + password -- server confirms identity |
| Authorization | Boarding pass says Seat 12A, Economy -- not First Class lounge | JWT payload says role: "user" -- cannot access /admin/dashboard |
You cannot be authorized without being authenticated first. The system must know who you are before deciding what you may do.
3. Authentication flow
Client Server Database
------ ------ --------
1. Submit credentials ────────► 2. Extract email + password
(email, password) 3. Find user by email ────────► SELECT * FROM users
4. Compare password hash ◄────── return user row
5. Passwords match?
YES → create session/token
NO → 401 Unauthorized
6. Return token / set cookie
◄──────── receive token ───────
7. Store token (cookie/memory)
Key point: The server never stores or sees the plain-text password (after initial registration). It only compares hashes.
4. Authorization flow
Client Server
------ ------
1. Request protected resource ─► 2. Auth middleware: verify token → identify user
GET /admin/users (Authentication step -- "who?")
Authorization: Bearer <token> 3. Authz middleware: check user.role
role === "admin"?
YES → next() → send data
NO → 403 Forbidden
Authorization always follows authentication. The server pipeline:
Request → authenticate → authorize → route handler → response
5. Common authentication methods
| Method | How It Works | Best For |
|---|---|---|
| Username + Password | User submits credentials; server verifies hash | Traditional web apps |
| OAuth 2.0 | Delegate auth to Google/GitHub/Facebook; receive access token | Social login, third-party APIs |
| Single Sign-On (SSO) | One login for multiple services (SAML, OpenID Connect) | Enterprise / corporate apps |
| API Keys | Static secret string sent in header | Server-to-server, public APIs |
| Biometric | Fingerprint, face recognition (device-level) | Mobile apps, hardware tokens |
| Magic Link | Email a one-time login URL | Passwordless experiences |
| Multi-Factor (MFA) | Password + SMS/TOTP code | High-security applications |
6. Common authorization models
| Model | Description | Example |
|---|---|---|
| Role-Based (RBAC) | Assign roles; roles have permissions | admin can delete users; user cannot |
| Permission-Based | Directly assign fine-grained permissions | user:read, user:write, post:delete |
| Attribute-Based (ABAC) | Rules based on user/resource/environment attributes | "Allow if user.department === resource.department AND time < 5PM" |
| Access Control Lists (ACL) | Per-resource list of who can do what | File: read: [alice, bob], write: [alice] |
For most Node.js/Express apps, RBAC is the sweet spot -- simple to implement, easy to reason about.
7. Where auth fits in the Express request lifecycle
const express = require('express');
const app = express();
// 1. Body parsing (runs on ALL requests)
app.use(express.json());
// 2. Public routes -- no auth needed
app.post('/api/register', registerHandler);
app.post('/api/login', loginHandler);
// 3. Authentication middleware -- verifies identity
app.use('/api', authMiddleware); // everything below requires login
// 4. Authorization middleware -- checks permissions (per route)
app.get('/api/admin/users', authorize('admin'), getUsers);
app.get('/api/profile', getProfile); // any authenticated user
// 5. Error handling
app.use(errorHandler);
Order matters. Public routes go before the auth middleware. Protected routes go after.
8. HTTP status codes for auth
| Code | Name | Meaning |
|---|---|---|
| 200 | OK | Authenticated and authorized -- here is your data |
| 401 | Unauthorized | Authentication failed -- who are you? (misleading name) |
| 403 | Forbidden | Authenticated but not authorized -- you cannot do this |
| 419 | (custom) | Session expired (used by some frameworks like Laravel) |
| 429 | Too Many Requests | Rate-limited -- too many login attempts |
9. Authentication vs authorization in code
// --- AUTHENTICATION: verify identity ---
async function login(req, res) {
const { email, password } = req.body;
// 1. Find user
const user = await User.findOne({ email });
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
// 2. Compare password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(401).json({ error: 'Invalid credentials' });
// 3. Issue token (authentication complete)
const token = jwt.sign({ userId: user._id, role: user.role }, SECRET, {
expiresIn: '24h',
});
res.json({ token });
}
// --- AUTHORIZATION: check permissions ---
function authorize(...allowedRoles) {
return (req, res, next) => {
// req.user was set by auth middleware (authentication already passed)
if (!allowedRoles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden: insufficient permissions' });
}
next();
};
}
// Usage: only admins can delete users
router.delete('/users/:id', authorize('admin'), deleteUser);
10. Security principles to remember
- Defense in depth -- never rely on a single layer. Combine authentication, authorization, input validation, and rate limiting.
- Least privilege -- give users the minimum permissions they need.
- Fail closed -- if auth check fails or errors, deny access (never default to allow).
- Never trust the client -- always validate on the server. A hidden button in the UI is not security.
- Separation of concerns -- keep authentication logic separate from authorization logic.
11. Key takeaways
- Authentication = identity verification (who); authorization = permission checking (what).
- Authentication happens first; authorization depends on knowing who the user is.
401means "not authenticated";403means "authenticated but not authorized."- RBAC (Role-Based Access Control) is the most common model in Node.js backends.
- Public routes go before auth middleware; protected routes go after.
Explain-It Challenge
Explain without notes:
- A user logs in successfully but gets a
403when accessing/admin. What happened, and which layer (authentication or authorization) rejected the request? - Why does authentication always come before authorization in the middleware chain?
- A junior developer protects an admin page by hiding the link in the frontend. Why is this not real security?
Navigation: <- 3.14 Overview | 3.14.b -- Password Security with Bcrypt ->