---
title: "Authentication vs Authorization: AuthN vs AuthZ (and Why 401 ≠ 403)"
description: "Authentication and authorization sound the same and get mixed up constantly, but they answer two different questions: who are you, versus what are you allowed to do. Here's the difference that actually prevents security bugs — credentials and the three factors that build MFA, roles and scopes and policies checked on every request, the classic 401-vs-403 trap, and how the exact same split governs API keys and tool permissions for LLM agents."
keywords: "authentication vs authorization, authn vs authz, 401 vs 403, MFA, multi-factor authentication, RBAC, roles and scopes, OAuth scopes, access control, permissions, API keys, JWT, security, system design, LLM agent permissions, tool authorization"
created_at: "2026-06-29T10:00:00"
post_type: "anonym_post"
content_type: "technical_article"
---

![Banner](./banner.webp)

<div style="display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: center; padding: 1rem 1.25rem; margin: 1.5rem 0 2rem; background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%); border: 1px solid #e2e8f0; border-radius: 12px;">
  <span style="font-size: 0.95rem; font-weight: 600; color: #475569; margin-right: 0.25rem;">Prefer to watch?</span>
  <a href="https://youtube.com/shorts/S5iIdIjNixs" target="_blank" rel="noopener noreferrer" style="display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.55rem 1rem; border-radius: 8px; background: #ff0000; color: #ffffff; font-size: 0.875rem; font-weight: 600; text-decoration: none;">▶ AuthN vs AuthZ in 90 seconds</a>
  <a href="https://t.me/SoftwareEngineerBlog" target="_blank" rel="noopener noreferrer" style="display: inline-flex; align-items: center; gap: 0.4rem; padding: 0.55rem 1rem; border-radius: 8px; background: #229ed9; color: #ffffff; font-size: 0.875rem; font-weight: 600; text-decoration: none;">✈ Telegram</a>
</div>

They sound almost identical, they're both abbreviated "auth," and engineers blur them constantly — which is a problem, because the confusion shows up later as security bugs. **Authentication** and **authorization** answer two completely different questions, and they happen in a fixed order.

The one-line mental model:

- **Authentication (AuthN)** asks: **who are you?** You prove your identity.
- **Authorization (AuthZ)** asks: **what are you allowed to do?** The system checks your permissions.

Think of a hotel. Showing your passport at the front desk to get a key card is **authentication** — proving who you are. The key card then opening *your* room but not the penthouse or the staff office is **authorization** — what that identity is permitted to access. You always show the passport first; the door checks come after.

---

## Authentication: prove who you are

Authentication is how you prove your identity to a system. You hand over **credentials** — a password, a one-time code, a fingerprint — the server verifies them, and if they check out, it now knows it's really you.

The critical property: **authentication always happens first.** No identity, nothing else matters. You can't decide what someone is allowed to do until you know *who they are*.

```http
POST /login HTTP/1.1
Content-Type: application/json

{ "email": "vahid@example.com", "password": "•••••••••" }

200 OK
{ "token": "eyJhbGc..." }   ← proof of identity for the next requests
```

That token (a session cookie or a JWT) is the server saying "I've confirmed who you are — show me this on every future request so I don't have to re-check your password each time."

---

## The three factors (and how you get MFA)

There are exactly three *kinds* of evidence you can use to prove identity, and security people call them **factors**:

- **Something you know** — a password, a PIN, a passphrase.
- **Something you have** — your phone, a hardware security key, a TOTP authenticator app.
- **Something you are** — a fingerprint, a face scan, an iris — biometrics.

A password alone is **single-factor**. The moment you combine **two or more different factors** — say a password *plus* a code from your phone — you get **multi-factor authentication (MFA)**. The reason MFA is so much stronger is simple: an attacker who phishes your password still doesn't have your physical phone or your fingerprint. They'd have to defeat two independent categories at once.

> Rule of thumb: a stolen password is one factor compromised. MFA means one stolen factor isn't enough.

---

## Authorization: decide what you can do

Once the server knows *who* you are, **authorization** decides *what* you're allowed to do. Same identity, different permissions: can this user read this record, edit that one, delete the other?

Authorization is usually expressed as one of:

- **Roles (RBAC)** — `admin`, `editor`, `viewer`. The role carries a bundle of permissions.
- **Scopes** — fine-grained grants like `repo:read` or `billing:write`, common in OAuth tokens.
- **Policies** — rules evaluated against attributes ("a user may edit a document only if they own it").

```http
DELETE /posts/42 HTTP/1.1
Authorization: Bearer eyJhbGc...      ← we know WHO you are (authenticated)

403 Forbidden
{ "error": "viewer role cannot delete posts" }   ← but you're not ALLOWED (authorization)
```

The key thing people forget: **authorization is checked on every single request**, not just once at login. Being logged in doesn't mean you're allowed to do the next thing you try. Identity is established once; permission is verified every time.

---

## The twist everyone trips on: 401 vs 403

Here's where the confusion turns into actual bugs. HTTP has two status codes that map *exactly* onto this split — and one of them is misnamed.

- **`401 Unauthorized`** → we **don't know who you are** yet. Your credentials are missing or invalid. This is an **authentication** failure. The correct fix is *log in*.
- **`403 Forbidden`** → we **know exactly who you are**, you're just **not allowed** to do this. This is an **authorization** failure. Logging in again won't help — you lack the permission.

The trap is the name: HTTP labeled the *authentication* error "**Unauthorized**," even though it's really about authentication, not authorization. So `401` = "authenticate, please," and `403` = "authenticated, but forbidden." Same request, completely different failure, completely different fix.

<table>
  <thead><tr><th></th><th>401 Unauthorized</th><th>403 Forbidden</th></tr></thead>
  <tbody>
    <tr><td>Really means</td><td>Not authenticated</td><td>Authenticated, not permitted</td></tr>
    <tr><td>Which "auth"</td><td>AuthN (who are you?)</td><td>AuthZ (what can you do?)</td></tr>
    <tr><td>Does the server know you?</td><td>No</td><td>Yes</td></tr>
    <tr><td>How to fix it</td><td>Log in / send valid credentials</td><td>Get the right role/scope — re-login won't help</td></tr>
  </tbody>
</table>

---

## The same split in AI / LLM systems

If you build with LLMs and agents, this isn't abstract trivia — it's the security model for your whole stack, and the two halves stay perfectly distinct.

**Authentication** in an AI app is mostly about **API keys and tokens**. Your service authenticates to the model provider with a secret key; your users authenticate to your app with sessions or JWTs. The hard-won lesson here is that those provider keys are *identity* — leak one and someone is now *you* to the LLM provider, spending your budget. That's why keys belong in secret stores, never in client code or environment variables that leak through process inspection.

**Authorization** is where agentic systems get genuinely interesting. An LLM agent with tools is an identity that can *act* — call functions, hit internal APIs, run queries. The dangerous shortcut is letting the agent inherit broad, ambient permissions just because it's "trusted." It shouldn't be. The same RBAC-and-scopes thinking applies:

- Give each agent or tool the **narrowest scope** it needs — read-only where it only needs to read, a single namespace where it only touches one dataset.
- Authorize **per tool call**, on every request, exactly like a `403` check — "is *this* identity allowed to run *this* tool with *these* arguments?" — rather than trusting the model's intent.
- Keep the **user's** authorization separate from the **agent's**: a user asking a question should never let the agent do something the user themselves isn't permitted to do (the classic *confused deputy* problem).

Protocols like **MCP** and OAuth-scoped tokens exist precisely so an agent authenticates once but is authorized narrowly, per action. Same two questions as a plain web app — *who are you?* then *what may you do?* — just with a model in the loop that can take actions on its own.

---

## Authentication vs authorization at a glance

<table>
  <thead><tr><th></th><th>Authentication (AuthN)</th><th>Authorization (AuthZ)</th></tr></thead>
  <tbody>
    <tr><td>Question it answers</td><td>Who are you?</td><td>What are you allowed to do?</td></tr>
    <tr><td>What it checks</td><td>Credentials (the three factors)</td><td>Roles · scopes · policies</td></tr>
    <tr><td>When it runs</td><td>First, to establish identity</td><td>On every request, after identity</td></tr>
    <tr><td>Fails with</td><td>401 Unauthorized</td><td>403 Forbidden</td></tr>
    <tr><td>Stronger by</td><td>Adding factors (MFA)</td><td>Least privilege, narrow scopes</td></tr>
  </tbody>
</table>

---

## The verdict

- **Authentication is who you are** — prove identity with credentials, and combine two or more **factors** to get MFA.
- **Authorization is what you're allowed to do** — enforced with **roles, scopes, and policies**, and checked on **every request**, not just at login.
- The order is fixed: **authenticate first, then authorize.** Identity, then permissions.
- Remember the misnomer: **`401` is an authentication error** ("we don't know you — log in"), **`403` is an authorization error** ("we know you, you're just not allowed"). Reaching for the wrong one is how access-control bugs ship.

Get these two straight and your whole security model gets simpler — for a REST API, an OAuth flow, or an LLM agent reaching for a tool. Which one bit you last?

> Want the 90-second visual version? [Watch the reel.](https://youtube.com/shorts/S5iIdIjNixs)
