What's the actual difference between authentication and authorization, and where do systems commonly conflate them?
Authentication proves who you are; authorization decides what you're allowed to do — conflating the two is how 'logged in' quietly becomes 'allowed to do anything.'
LEARN BY DOINGPut your intuition to work3 steps
What's the actual difference between authentication and authorization, and where do systems commonly conflate them?
Use the lesson’s examples and explanation to check your idea.
Personal reflection. Nothing is sent to an AI service.#The one-sentence distinction
Authentication (AuthN) answers "who are you?" Authorization (AuthZ) answers "are you allowed to do this specific thing?" They run one after another in almost every request, but they are answering completely different questions, checked against completely different data, and failing in completely different ways. A surprising share of real-world security bugs — including most IDOR (Insecure Direct Object Reference) vulnerabilities — come from a system that gets authentication right and simply forgets to ask the second question.
#Authentication: proving identity
Authentication is the process of verifying that a principal (a user, a service, a device) is who it claims to be. It happens once per session or per request, typically at the edge of the system: a login form checking a password, a middleware validating a JWT signature, an mTLS handshake verifying a client certificate. The output of a successful authentication step is an identity — a user ID, a service name, a set of claims — that the rest of the system can trust was verified, not a decision about what that identity is allowed to touch.
#Authorization: granting permission
Authorization takes that already-trusted identity and asks whether it has permission to perform a specific action on a specific resource. This is inherently contextual: the same authenticated user is authorized to read their own invoice and not authorized to read someone else's, even though both requests hit the same endpoint with the same valid session. Authorization checks therefore live closer to the business logic than authentication checks do — they need to know not just who is asking, but what they're asking for.
| Authentication | Authorization | |
|---|---|---|
| Question | Who are you? | What can you do? |
| Input | Credentials (password, token, certificate) | Verified identity + requested resource/action |
| When it runs | Once, at session/request start | On every sensitive operation, resource by resource |
| Typical failure | Wrong person gets in | Right person does something they shouldn't |
| Correct HTTP status | 401 Unauthorized | 403 Forbidden |
| Common mechanism | Password hash comparison, JWT signature check, OAuth flow | Role checks (RBAC), attribute checks (ABAC), ownership checks (ACL) |
#Where systems actually conflate the two
The distinction sounds obvious in the abstract, but codebases blur it constantly:
- IDOR bugs. An endpoint like
GET /api/invoices/:idchecks that the caller has a valid session (authentication) but never checks that:idbelongs to that caller (authorization). Any logged-in user can enumerate other users' invoices just by changing the ID in the URL. - 401 vs 403 vs 404 confusion.
401means not authenticated (no or expired credentials — log in again);403means authenticated but not allowed. When a resource exists but the caller may not see it,403confirms that it exists — returning404instead leaks less, because the caller can't tell "forbidden" from "not there." And returning403when the real problem is an expired token trains client code to treat "forbidden forever" the same as "log in again." Getting this wrong doesn't just look sloppy — it breaks retry logic and token-refresh flows. - Trusting stale claims. A JWT issued at login says
role: admin. The user is demoted an hour later, but the token is valid for 24 hours and nothing re-checks the role against the database, so the authorization decision is made against data that authentication alone can't refresh. - Client-side-only authorization. Hiding an "Edit" button in the UI for non-owners is a UX nicety, not a control — if the API behind it doesn't independently re-check ownership, anyone with a browser console can call it directly.
- Multi-tenancy leaks. A query authenticates the user and authorizes them against a
resource_id, but forgets to also scope the query bytenant_idororg_id, so a valid, authorized-looking request from Tenant A can still return Tenant B's data if the IDs happen to collide or be guessable.
In every one of these, authentication worked exactly as designed. The bug is always the missing or misplaced second check.
#The request lifecycle, visually
Notice that authorization is drawn as a second, independent gate — not a side effect of authentication passing. In a well-built system, every handler that touches a specific resource re-asks the authorization question explicitly, even though the identity was already verified upstream. Frameworks that bake "logged in" and "allowed" into the same middleware call are the ones most likely to produce IDOR bugs, because the second question silently gets treated as already answered.
🧠 Check your understanding
0/3 · 0/3 answered1. An endpoint returns a user's own order details when they're logged in, but a logged-in user can also fetch other users' orders by changing the order ID in the URL. What kind of bug is this?
2. Which HTTP status code most precisely communicates 'you are correctly identified, but you are not allowed to perform this action'?
3. Why is hiding an 'Edit' button in the UI for non-owners not a real access control?