Sign in
TodayMapLearnPracticeReview
Library
11 MINcoreSecurityNot started

Authorization

Authorization (AuthZ) decides what an authenticated user is allowed to do. Two dominant models: RBAC (role-based — "users with the admin role can delete") and ABAC (attribute-based — "users in the finance dept can view invoices over $10k from their region"). AuthN establishes identity; AuthZ uses it. Every request must be authorized, not just authenticated, or authenticated users can act as each other.

Why this matters

Authorization bugs are the most common class of web vulnerability after authentication failures. The classic mistakes: trusting client-side authorization ("hide the button"), checking authorization on read but not write, missing per-resource checks ("user can edit their own profile" becomes "user can edit anyone's profile"). The 2021 LinkedIn scrape and countless IDOR (Insecure Direct Object Reference) bugs are all authorization failures. AuthZ must be enforced server-side, on every request, with per-resource checks. There are no shortcuts.

Prerequisites
  • Authentication
Related
  • Authentication
  • OAuth 2.0
  • Gatekeeper Pattern
Used in
  • Authentication
  • Gatekeeper Pattern
  • OAuth 2.0
Lesson

How it works

Authorization answers: "given this authenticated identity, what can they do?" It runs after authentication on every request.

Two models dominate:

  • RBAC (Role-Based Access Control): users have roles (admin, editor, viewer); roles have permissions (delete_post, edit_post, view_post). Authorization = check the user's role has the required permission. Simple, common, sufficient for most apps.

  • ABAC (Attribute-Based Access Control): rules combine attributes of the user, the resource, the action, and the environment. "A user in the finance department can view invoices under $10k from their region during business hours." More expressive, more complex.

RBAC is the right starting point; ABAC when RBAC isn't expressive enough. Most systems use RBAC for coarse permissions and add per-resource checks for fine-grained control.

The most common authorization bug is missing per-resource checks. Consider:

  • A user has edit_post permission.
  • They send POST /posts/999/edit.
  • Does the user own post 999? If you didn't check, they just edited someone else's post.

This is an IDOR (Insecure Direct Object Reference) vulnerability, and it's everywhere. The fix is mandatory per-resource authorization: every request that touches a specific resource checks not just "can the user do this action" but "can the user do this action to THIS resource."

Implementation: in the controller, fetch the resource first, then check user.id == resource.owner_id (or a more complex policy) before performing the action. If the check fails, return 404 (not 403, to avoid leaking the resource's existence).

The pattern of "hide the edit button if not owner" is necessary for UX but never sufficient for security — the user can craft the request manually. Server-side enforcement is the only check that matters.

Authorization logic spread across controllers rots. The pattern that scales:

  • Centralize policy: a single authorization layer (a policy engine or middleware) that all requests pass through. It consults the user's roles, the resource's owner, and the action being performed.
  • Declarative policy: define policies as data, not code. Tools like OPA (Open Policy Agent), AWS IAM, and Casbin express policies in a DSL that's auditable and version-controlled.
  • Deny by default: if no policy explicitly permits an action, deny it. This prevents "I forgot to check" bugs from becoming security holes.
  • Audit log: every authorization decision is logged. "alice viewed post 42 at 10:42:03." Essential for incident response and compliance.

For microservices, a service mesh or API gateway can enforce coarse authorization ("is the user authenticated? does their token have the required scope?") centrally, with fine-grained per-resource checks in the service. This splits the work: the gateway handles cross-cutting concerns; the service handles business logic.

Client-side authorization is decoration, not security

Hiding the admin button when the user isn't an admin is good UX — it prevents confusion. But it's not security. The user can always craft the HTTP request manually. Authorization must be enforced server-side on every request, regardless of what the UI shows. The same applies to mobile apps, SPAs, and any client. The client is untrusted; the server is the only place authorization can be enforced. Treat every client as potentially malicious.

In API and OAuth contexts, authorization often uses scopes — strings that grant specific permissions. A token might have scopes read:posts write:posts but not delete:posts. The API checks the token's scopes before performing each action.

Scopes are a middle ground between RBAC and ABAC:

  • Like RBAC: a fixed set of named permissions.
  • Like ABAC: scoped to a specific resource or action.

The principle of least privilege: grant only the scopes needed for the task. An app that reads your GitHub repos should request read:user public_repo, not repo (which would grant write to all repos). Most OAuth flows let the user see and approve the requested scopes.

Scopes must be enforced server-side, just like RBAC — checking the scope on read but not write is a common bug.

Check yourself
core

Your web app hides the "Delete" button when the user isn't an admin. Is the system secure?

Pick one answer.

Check yourself
interview

A user with `edit_post` permission sends `POST /posts/999/edit`. Post 999 belongs to another user. What went wrong if the edit succeeds?

Pick one answer.

Check yourself
advanced

When would you choose ABAC over RBAC?

Pick one answer.

Engineering mental model

Mental model. Think of Authorization as a deliberate boundary in a system. The boundary exists because something becomes harder to manage when everything is done in one place: latency, scale, failure isolation, consistency, cost, or team ownership. The useful question is not “what does Authorization mean?” but “what pressure makes this boundary worth introducing, and what new failure mode does it create?”

Design lens

Before choosing Authorization, name the workload, the critical user path, the dominant bottleneck, the failure you are trying to absorb, and the trade-off you are willing to accept. If you cannot name those five things, the design is probably premature.

Original NO CAP systems visual for Authorization.
Image unavailable. Original NO CAP systems visual for Authorization.
Authorization: a compact system-thinking visual.— Original NO CAP visual.
Authorization: Bearer <access-token>

GET /v1/authorization
Host: api.example.com
A minimal engineering sketch for reasoning about Authorization.

Back-of-the-envelope reasoning

Numerical lens: write down traffic, payload size, read/write ratio, peak multiplier and durability target before choosing a component. The numbers should justify the architecture.

Interactive sandboxdeterministic

Interactive thought experiment: Authorization

Change the variables below and predict what breaks first in Authorization. The production lab can later reuse these same inputs.

System pressure6%
Try this

Change one variable at a time. Predict the failure mode first, then move the slider and see whether your mental model matches the simplified system response.

Hint

If you are stuck on Authorization, start by drawing the request path and marking every network hop, stateful component, queue, cache and failure boundary. Then estimate where the system will saturate.

Check yourself
solid

You increase traffic by 10× in a system using Authorization. What should you inspect first?

Pick one answer.

Check yourself
interview

Which statement is the safest engineering habit when using Authorization?

Pick one answer.

Try this
interview

You have dashboards for traffic, latency, errors and saturation. You can change the architecture, but every change has operational cost.

Production scenario: your system uses Authorization, traffic suddenly spikes, and p99 latency doubles. What is your first move?

Interview drill

Answer this without notes: When would you choose Authorization, and when would you intentionally avoid it? Mention at least one bottleneck it addresses, one failure mode it introduces, and one alternative. Then quantify the workload you are designing for.

Engineering lens

For Authorization, define the trust boundary first. Identify who is allowed to perform each action, where credentials live, how they expire, and what a compromised credential can reach.

Numerical sanity check

A practical blast-radius question: if one credential is compromised, how many users, services, records or regions could it affect? Prefer designs where that number is deliberately bounded.

Check yourself
interview

Do not optimize for a memorized definition. Reason from the workload and failure mode.

What is the smallest trust boundary you would enforce for Authorization, and what would you log so a suspicious action can be investigated later?

Pick one answer.

Trade-offs

What you gain, what you pay

Pros
  • +Server-side enforcement is the only real security control.
  • +RBAC is simple, fast, easy to audit.
  • +ABAC is expressive enough for complex, regulated environments.
  • +Centralized policy (OPA, IAM) makes authorization auditable.
Cons
  • −Missing per-resource checks create IDOR vulnerabilities.
  • −ABAC is harder to audit and slower to evaluate.
  • −Distributed authz logic across controllers rots quickly.
  • −Deny-by-default can break features if policies are incomplete.
Failure modes

How this breaks in production

  • Missing per-resource authorization (IDOR).
  • Trusting client-side checks (hide-the-button).
  • Checking authorization on read but not write.
  • Broad scopes granted "just in case" — violates least privilege.
Common mistakes

Don't fall into these traps

  • •Hiding UI elements instead of enforcing server-side.
  • •Forgetting per-resource checks beyond role checks.
  • •Spreading authorization logic across controllers instead of centralizing.
  • •Granting broader scopes than needed (violating least privilege).
Where you see it

Real systems using this

AWS IAM (policies as data, evaluated per request).Open Policy Agent (OPA) for service authorization.OAuth scopes for API permissions.
Teardowns

How real systems implement this

  • AWS IAM — Policy-based authorization. Policies are JSON documents expressing allow/deny rules over resources and actions. Evaluated on every API call. Deny by default.
  • Open Policy Agent (OPA) — General-purpose policy engine with the Rego DSL. Used to centralize authorization across microservices. Policies are versioned, auditable data — not code scattered through controllers.
Interview prompts

Practice saying it out loud

  • Q1What's the difference between authentication and authorization?
  • Q2What is an IDOR vulnerability, and how do you prevent it?
  • Q3Compare RBAC and ABAC. When would you use each?
  • Q4Why must authorization be enforced server-side, not in the client?
Research

Further reading & references

System Design Primer
Open source
ByteByteGo — Scale from zero to millions
ByteByteGo
System Design Tutorial
GeeksforGeeks
System Design Roadmap
roadmap.sh
Security reference
Reference
Security reference
Reference
Security reference
Reference

Core explanations are original NO CAP material. External references are provided for deeper study and standards.

What next?

Mark as understood once the mental model clicks.

Next recommended

Authentication