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?"
AnalogyShowing your ID card at the doorHaving a VIP pass to backstage
InputCredentials (username, password, token)Identity + resource being accessed
OutputConfirmed identity or rejectionAllow or deny the action
WhenBefore authorization -- always firstAfter authentication succeeds
Failure code401 Unauthorized (really means unauthenticated)403 Forbidden

2. Real-world analogy: the airport

StepAirportWeb App
AuthenticationShow passport at check-in -- prove you are John DoePOST /login with email + password -- server confirms identity
AuthorizationBoarding pass says Seat 12A, Economy -- not First Class loungeJWT 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

MethodHow It WorksBest For
Username + PasswordUser submits credentials; server verifies hashTraditional web apps
OAuth 2.0Delegate auth to Google/GitHub/Facebook; receive access tokenSocial login, third-party APIs
Single Sign-On (SSO)One login for multiple services (SAML, OpenID Connect)Enterprise / corporate apps
API KeysStatic secret string sent in headerServer-to-server, public APIs
BiometricFingerprint, face recognition (device-level)Mobile apps, hardware tokens
Magic LinkEmail a one-time login URLPasswordless experiences
Multi-Factor (MFA)Password + SMS/TOTP codeHigh-security applications

6. Common authorization models

ModelDescriptionExample
Role-Based (RBAC)Assign roles; roles have permissionsadmin can delete users; user cannot
Permission-BasedDirectly assign fine-grained permissionsuser: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 whatFile: 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

CodeNameMeaning
200OKAuthenticated and authorized -- here is your data
401UnauthorizedAuthentication failed -- who are you? (misleading name)
403ForbiddenAuthenticated but not authorized -- you cannot do this
419(custom)Session expired (used by some frameworks like Laravel)
429Too Many RequestsRate-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

  1. Defense in depth -- never rely on a single layer. Combine authentication, authorization, input validation, and rate limiting.
  2. Least privilege -- give users the minimum permissions they need.
  3. Fail closed -- if auth check fails or errors, deny access (never default to allow).
  4. Never trust the client -- always validate on the server. A hidden button in the UI is not security.
  5. Separation of concerns -- keep authentication logic separate from authorization logic.

11. Key takeaways

  1. Authentication = identity verification (who); authorization = permission checking (what).
  2. Authentication happens first; authorization depends on knowing who the user is.
  3. 401 means "not authenticated"; 403 means "authenticated but not authorized."
  4. RBAC (Role-Based Access Control) is the most common model in Node.js backends.
  5. Public routes go before auth middleware; protected routes go after.

Explain-It Challenge

Explain without notes:

  1. A user logs in successfully but gets a 403 when accessing /admin. What happened, and which layer (authentication or authorization) rejected the request?
  2. Why does authentication always come before authorization in the middleware chain?
  3. 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 ->