Chapter 37·Intermediate·8 min read
Authorization Explained: Deciding What a User Can Do
A plain-English guide to authorization — how it differs from authentication, models like roles (RBAC) and attributes (ABAC), enforcing checks on the server, and the principle of least privilege.
June 30, 2026
Authentication told us who a request is. But knowing someone is alice@example.com doesn't mean she can delete any account she likes. Authorization is the second gate: deciding what an identified user is allowed to do. It's where a lot of real security lives — and where a lot of real breaches come from when it's done loosely.
Authorization vs authentication
The two are a sequence, not a synonym:
- Authentication establishes identity (the previous chapter).
- Authorization takes that identity and checks permissions for the specific action requested.
A logged-in user is authenticated for everything and authorized for only some things. Alice can edit her own profile (authorized) but not delete the company database (not authorized) — same identity, different permissions per action.
RBAC: permissions through roles
The most common model is Role-Based Access Control (RBAC). Instead of assigning permissions to each user individually, you define roles, attach permissions to roles, and give users roles:
| Role | Can do |
|---|---|
| Viewer | Read content |
| Editor | Read + write content |
| Admin | Everything, including manage users |
RBAC is popular because it scales with people. Onboard an editor by giving them the editor role — no need to wire up dozens of individual permissions. Change what editors can do in one place and it applies to everyone. For most applications, RBAC is the right starting point. The dedicated RBAC chapter in the auth guide goes deeper.
ABAC: when roles aren't enough
Roles describe categories of users, but some rules depend on context that roles can't capture: "a user can edit a document only if they own it," or "approvals over $10,000 require a manager in the same region." For these, Attribute-Based Access Control (ABAC) decides access from attributes of the user, the resource, and the situation.
You don't have to choose one purely; the common pattern is RBAC for the coarse "what kind of user are you" and attribute checks for the fine "is this your thing."
Enforce on the server — always
Here is the rule that, if you remember nothing else, prevents whole classes of breach: authorization must be enforced on the backend, on every request.
It is tempting to think hiding a "Delete" button in the UI protects the action. It does not. The button is gone, but the underlying API endpoint is still reachable — anyone can call it directly, bypassing your interface entirely.
This is one of the most common real-world vulnerabilities: an endpoint that checks who you are but forgets to check whether you're allowed to touch the specific resource. Check both, every time, on the server.
The principle of least privilege
A guiding default that limits the blast radius of mistakes: least privilege — grant each user (and each service) the minimum access needed to do their job, and nothing more.
In practice that means default to deny: access is refused unless a rule explicitly grants it. When something goes wrong — a compromised account, a bug, a malicious insider — least privilege ensures the damage is bounded to what that account could narrowly do, rather than everything. It's the difference between a leak and a catastrophe.
Recap
- Authorization decides what an identified user can do — it runs after authentication, per action.
- RBAC groups permissions into roles assigned to users — simple and scalable, the common default.
- ABAC uses attributes and context (like ownership) for rules roles can't express; most systems combine both.
- Enforce authorization on the server, every request — hiding UI controls is UX, never security.
- Follow least privilege: default to deny and grant only what's needed, to bound the damage when things fail.
We can serve data to the right people. But serving the same data repeatedly from the database is wasteful and slow. Caching fixes that. Continue to Caching in the Backend.