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.