Authorization models and where auth lives

Lesson 6.3 · 5 min read · by Vinay C

We'll cover the following

Models for deciding what a user may do

ModelHow it decidesStrengthWeakness
RBAC (role-based)Permissions attach to roles, users get roles: viewer reads, editor reads and writes, admin also deletesSimple, easy to explain and audit. The default answer.Role explosion: "managers approve expenses under $5K in their own department" needs a role per department and amount band
ABAC (attribute-based)Rules over attributes of the user, the resource and the contextVery flexible and fine-grainedHarder to reason about, test and audit
allow if user.department == resource.department
     AND amount < 5000
     AND time is within business hours
  • ABAC rules are usually run through a policy engine such as OPA, so they live in one place instead of being scattered through service code.
  • ReBAC (relationship-based) in one line: access follows relationships, like "you can edit this doc because you are in the team that owns the folder". Google Drive style, from Google Zanzibar, open-source versions such as SpiceDB.
  • Rule of thumb: RBAC for coarse permissions, plus ownership or attribute checks where RBAC is not enough. Most real systems are exactly that.

Where auth lives: gateway or services?

Both, doing different jobs.

LayerJob
GatewayAuthentication plus coarse checks: verify the token (signature, expiry, denylist), reject bad tokens early, check broad scopes ("does this token allow orders:read at all?"), apply rate limits. Then forward the verified identity downstream as a header or a short internal token.
Each serviceFine-grained, resource-level authorization. The service owns the data, so it decides "can user 42 view order 7?"
  • Why not gateway-only: the gateway cannot see business data (it does not know order 7 belongs to someone else).
  • Zero trust: internal services should not blindly trust whatever arrives from inside the network. Use mTLS between services so one compromised service cannot impersonate anyone.
  • When rules get complex, centralize policy in an engine like OPA so every service asks the same source of truth instead of re-implementing the rules.

IDOR / BOLA: the check people forget

User 42 is logged in with a perfectly valid token and calls GET /orders/7, an order that belongs to user 99. The token is valid, so the gateway lets it through. Only the service can block it, because only it knows who owns order 7.

  • The check: after loading the order, compare order.owner_id with the user id from the verified token. Or make the query do it: WHERE id = 7 AND owner_id = 42, so a foreign order is simply not found.
  • The bug has a name: IDOR (insecure direct object reference), also called BOLA (broken object level authorization). It is #1 on the OWASP API Security list. It happens when a service checks "is the token valid?" but never "is this your object?".
  • Take the user id from the verified token, never from the request. If the client sends user_id=42 in the body, an attacker just changes it.
  • Return 404, not 403, for objects the user should not know exist. A 403 confirms "order 7 exists, but it is not yours".
  • Random ids (UUIDs) are harder to guess than sequential ids, but only a speed bump. The ownership check is the real fix.
  • Test for it: log in as user A and request user B's ids. Many breaches come from this one missing check.
Check yourselfUser 42 has a valid token and calls GET /orders/7, an order owned by user 99. Which layer should block it, and what does it check?

The service that owns orders. It compares order.owner_id with the user id taken from the verified token (or queries with WHERE id = 7 AND owner_id = 42), and answers 404 so it does not reveal that the order exists. This is IDOR / BOLA, OWASP API #1.

Summary: say it in the interview

  • "The gateway authenticates and enforces coarse scopes. Services enforce resource-level authorization, RBAC plus ownership checks, and I would centralize policy in something like OPA if the rules get complex."