~/VibeHandbook
$39

Chapter 18 · 04

Authentication vs authorization: the endpoint everyone can call

These two words sound alike and the difference is where real breaches live.

  • Authentication is who are you? — logging in, proving identity.
  • Authorization is what are you allowed to do? — whether this logged-in user may perform this action on this data.

The AI is decent at authentication; libraries handle most of it. Authorization is where it routinely fails, because authorization is specific to your app's rules and the AI doesn't know them. The textbook disaster:

// VULNERABLE: checks you're logged in, but not WHO you are
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  res.json(db.getAllUsers()); // any logged-in user can hit this
});

That is "protected" — you must be logged in. But any logged-in user, including one who signed up thirty seconds ago, can call it and download every user's data. Authentication present, authorization absent. The fix is to check permission on the action itself:

// SAFE: confirms this user is actually an admin
app.get("/admin/export-all-users", requireLogin, (req, res) => {
  if (!req.user.isAdmin) return res.status(403).send("Forbidden");
  res.json(db.getAllUsers());
});

Think of two gates a request must pass. Authentication asks "are you logged in?"; authorization asks "are you allowed to do this?" The breach happens when an app builds the first gate and forgets the second:

  request ──▶ ┌──────────────────┐  ──▶ ┌──────────────────┐  ──▶  action
              │ AUTHENTICATION   │      │ AUTHORIZATION     │       runs
              │ "logged in?"     │      │ "may THIS user    │
              │                  │      │  do THIS action?" │
              └──────────────────┘      └──────────────────┘
                  ✓ most apps              ✗ often missing
                    build this               → the breach

  EXAMPLE — /admin/export-all-users
     logged-in user  ──▶ [auth ✓] ──▶ [ no authz check ] ──▶ ALL users dumped
     logged-in user  ──▶ [auth ✓] ──▶ [ isAdmin? → 403  ] ──▶ blocked  ✓

The same trap appears in miniature everywhere: an endpoint that returns order #1234 without checking the order belongs to the requesting user. Anyone can change the number in the and read someone else's order. The rule is unglamorous and absolute: check authorization on every endpoint that touches data, and don't trust an ID from the client. Don't assume a hidden URL is safe because it's hidden — "nobody knows this exists" is not a security control.

Want it offline?

Get the PDF + EPUB + downloadable prompt library + version updates.

$ Get the PDF — $39