Warming up the neural circuits...
By the end of this chapter you will:
Authentication and Authorization are two different problems that most beginners (and a shocking number of production systems) confuse. Mix them up and you ship a CVE. This chapter draws the line — permanently.
You walk up to a nightclub. Two things happen:
Authentication = identity. Authorization = permissions. You can be authenticated without being authorized. You cannot be authorized without being authenticated (unless you allow anonymous access, which is a deliberate choice).
| Model | How it works | Example | Best for |
|---|---|---|---|
| RBAC (Role-Based) | Assign permissions to roles. Assign roles to users. | admin role → can delete users. editor role → can edit posts. | Most applications. Simple, well-understood, easy to audit. |
| ABAC (-Based) | Evaluate attributes of user, resource, and environment against policies. | "User can edit document IF user.department == document.department AND document.status == 'draft'" | Complex enterprise systems, compliance-heavy environments. |
| ReBAC (Relationship-Based) | Permissions based on relationships between entities. | "Alice can view this document because Alice is a member of the 'Engineering' team and the document is shared with that team." | Social networks, collaboration tools (Google Docs), multi-tenant SaaS. |
// RBAC — simplest, most common
function canDeletePost(user: User, post: Post): boolean {
if (user.role === 'admin') return true;
if (user.role
90% of applications never need more than RBAC. The moment you hear "well, editors should be able to edit posts in their own department but not others, unless the post is flagged..." — that's time for ABAC. But don't start there. RBAC is simpler to implement, audit, and debug.
Every backend request passes through an auth layer. Here's where AuthN and AuthZ live:
Request → [TLS] → [Rate Limiter] → [AuthN Middleware: "Who are you?"]
→ [AuthZ Middleware: "Are you allowed?"] → [Controller/Handler] → [Response]req.user = { id, email, role }).req.user has permission for this specific action on this specific resource. This is where RBAC/ABAC/ReBAC lives.Given a bug, can you identify whether it's an AuthN or AuthZ problem?
| Bug | AuthN or AuthZ? | Why |
|---|---|---|
"Anyone can access /admin without logging in" | AuthN — the endpoint doesn't require authentication | No identity verification is happening |
| "Logged-in user can delete other users' posts by changing the post ID in the URL" | AuthZ — the user is authenticated but not authorized for that resource | Identity is verified, but permission check is missing (IDOR — Insecure Direct Object Reference) |
| "Expired token still works" | AuthN — token is broken | Identity verification is incomplete |
| "Regular user can promote themselves to admin" | AuthZ — role escalation without authorization check | Permission assignment is not gated |
Slack uses a sophisticated multi-model auth system:
channels:read, chat:write, users:read. This is ABAC applied to API access.Slack's lesson: auth scale isn't about performance — it's about policy complexity. When permissions depend on team membership, channel type, user role, AND guest restrictions, RBAC alone isn't enough. ReBAC models the relationship graph naturally.
| Mistake | Why it's wrong | What to do instead |
|---|---|---|
| AuthZ check only on the frontend | Hiding the "Delete" doesn't prevent a direct API call to DELETE /posts/42 | Enforce authorization on the backend. Every. Single. Endpoint. |
| Using role names as hardcoded strings everywhere | if (user.role === 'admin') scattered across 200 files. Adding a "super_admin" role requires touching all 200. | Centralize permission logic. Use a function: canDeletePost(user, post) or a policy engine. |
| Confusing 401 and 403 | 401 = "you didn't prove who you are" (missing token). 403 = "you proved who you are, but you're not allowed" (insufficient permissions). | Use the right status code. API consumers (and security scanners) depend on it. |
| No authorization on resource ownership | Any authenticated user can access /api/orders/:id — even orders belonging to other users | Check resource ownership: if (order.userId !== req.user.id && req.user.role !== 'admin') return 403 |
| Implementing auth from scratch | Auth is the hardest thing to get right. Every custom implementation has bugs. | Use battle-tested libraries: Passport.js, NextAuth, Auth0, Clerk, Supabase Auth. Never roll your own crypto or session management. |
authz.ts or permissions.ts) that exports functions like can(user, action, resource). Every endpoint calls this. Audit once, fix everywhere.req.user.role === 'admin' can access the route. Return 403 if not.admin, editor, viewer. Implement can(user, action, resource) where action is create, read, update, delete and resource is post. Write tests for each role-action combination.department and classification attributes and users have department, clearanceLevel, and role attributes. Implement a policy engine that evaluates rules like "editors can read documents in their department, but only if their clearance level >= the document's classification."What's the difference between authentication and authorization? Authentication verifies identity ("who are you?"). Authorization determines permissions ("what can you do?"). You can be authenticated without being authorized, but typically not authorized without being authenticated.
What status code should you return for a missing auth token vs insufficient permissions? 401 for missing/invalid auth (unauthenticated). 403 for valid auth but insufficient permissions (unauthorized).
What is RBAC? Role-Based Access Control. Users are assigned roles, roles are assigned permissions. Simplest and most common authorization model.
When would you choose ABAC over RBAC? When permissions depend on attributes of the user, resource, AND environment, not just role. Example: "Doctors can view patient records only for patients in their own department, only during their shift hours, and only if the patient hasn't opted out of data sharing." RBAC can't express this without an explosion of roles.
Explain IDOR and how to prevent it. Insecure Direct Object Reference — an attacker changes a resource ID in the URL (/orders/12345 → /orders/12346) and accesses another user's data because the backend doesn't check ownership. Prevent by: always verifying that the authenticated user owns or is authorized for the requested resource. Never rely on sequential/hidden IDs alone.
How would you design an authorization system that survives 5 years of product changes? Use a centralized policy evaluation function (can(user, action, resource)). Store roles/permissions in the database, not hardcoded in code. Support attribute-based policies from day one (even if initially only RBAC). Log every authorization decision for audit. Make the system testable in isolation. The key insight: separate the policy evaluation from the business logic so policies can evolve independently.
Authentication = identity. Authorization = permissions. They are different problems that require different middleware, different error codes (401 vs 403), and different testing strategies. RBAC covers 90% of applications. ABAC handles attribute-driven policies. ReBAC handles relationship-based access (social graphs, team structures). Auth checks must be centralized, fast, and auditable. Never implement auth from scratch. Fail closed.