Authentication vs authorization
Authentication is a fact you establish once. Authorization is a decision you make on every request — and that gap is where most access-control bugs live.

Someone asked me the difference between authentication and authorization today. The one-line answer is easy — authentication is who you are, authorization is what you're allowed to do — and that answer is exactly what causes the bugs, because it makes them sound like two halves of one step. They aren't. Authentication happens once and produces a fact. Authorization happens on every request and produces a decision.
Who this is for: an engineer who has a login working and is now adding the second user, the second tenant, or the first agent that acts on someone's behalf.
The one-line answer, and what it hides
A building pass is authentication: it proves you work here. It does not open the server room. Whether this pass opens that door, today, is a separate decision made by a separate system, and it is made every single time you hold the badge up — not once when HR issued it.
That is the part the one-line answer hides. The two differ less in what they ask than in when and how often. Authentication is an event: a password, a passkey, an OAuth exchange, and afterwards you hold a token that says an identity was proven. Authorization is a question asked continuously, and the correct answer changes while the session is still open — a role gets revoked, a document moves, a subscription lapses. A token minted twenty minutes ago knows none of that.
The failure isn't confusing the words
Nobody ships a bug because they mixed up two dictionary definitions. They ship it because the system treats passing the first check as implying the second.
It looks like a middleware that verifies the token on every route and stops there, so any authenticated user can call any endpoint. It looks like an admin button hidden with a CSS class, which is authorization implemented in the browser, where the person you're defending against lives. It looks like a record ID in the URL that nobody checks ownership of. It looks like internal services trusting any caller that reached them, because the network boundary was treated as an identity.
Say it plainly: a system that authenticates perfectly and authorizes nowhere is not partly secure. It is a system in which every user is effectively every other user, discovered the day someone changes a number in a URL.
Three questions, in order
- Who is asking? Authentication. Answer it once, properly, and don't re-implement it per service.
- May this identity do this action, to this resource, right now? Authorization. Answer it on every request, server-side, from the session — never from a tenant ID, role, or user ID the caller passed in. If the client supplies the input to the decision, you haven't built a control, you've built a suggestion.
- Can I show afterwards what was done and on whose authority? Audit. It isn't access control, but it's what makes the second question reviewable instead of theoretical — the same reason an agent needs a trail of what it did and why.
Most teams answer the first thoroughly, the second inconsistently, and the third never.
Where AI systems make this harder
Two things change when a model sits in the request path.
The first is delegation. Your agent authenticates once, as itself, then acts on behalf of many different users. If the tool call executes under the agent's service account, every user gets the union of everything the agent can reach — a privilege escalation nobody wrote, assembled out of parts that each looked reasonable. The user's authority has to travel with the request, and the tool has to enforce it at execution, not at prompt-writing time.
The second is retrieval, which has no concept of permission at all. Nearest-neighbour search ranks by similarity and answers whoever asks, which is why relevance is not access control: the filter belongs inside the query, taken from the authenticated session. And because the model can be talked into things, the decision cannot be the model's to make. A filter the model chooses is a filter an injected instruction can argue with.
What I deliberately leave out
Not covered here: OAuth flows, OIDC, RBAC versus ABAC versus ReBAC. Those are all implementations of question two, and picking between them before you've decided where the decision is made and what it reads from is how teams end up with a policy engine that still trusts a client-supplied tenant ID.
What's next
The harder version of this is delegation done properly — an agent acting for a user without inheriting everything that user can do, and without minting a token that outlives the task. That's the post I'd write next.
If you're wiring this into a product rather than reading about it, that's the kind of work I do.