Authorization

BackendTier 2 · build and shipPhase 3 · Give customers accounts

Deciding what you are allowed to do, once the system knows who you are. Different from proving who you are.

Knowing who someone is does not settle what they may do: a customer, a support agent, and an admin are all logged in but should not have the same powers. Treating every authenticated user alike would let anyone do anything. Authorization exists to decide, per action, whether this identity is permitted, so identity and permission stay separate questions.

Authentication answers "who are you." Authorization answers "what may you do." A logged-in customer may view their own orders but not someone else's; a staff account may issue refunds; an admin may change prices.

The backend checks authorization on sensitive actions every time, because knowing who you are does not mean you are allowed to do everything.

A junior PM, stuck

Security flagged that a normal customer can call the refund endpoint directly and get their money back with no staff involved. They want me to write the bug ticket saying what should have stopped it, and I cannot describe what is actually missing. The customer was logged in, so I do not see what went wrong.

This is an authorization hole, and it is a specific, common one. Logged in is not the same as allowed: the customer proved who they are, but nobody checked whether that role may issue refunds. I pulled the two real calls, the customer's and a staff member's, hitting the exact same endpoint. Read them side by side and you will see the gap yourself, then the ticket writes itself.

POST /api/orders/o_5512/refund, same call with two different tokens
Customer's token, the bug
5512 1.1
:
: 1569
88231,
1.1 200
{ "refunded": true, "order_id": "o_5512", "amount": 649 }
Staff token, the same endpoint
5512 1.1
: 4
88999,
1.1 200
{ "refunded": true, "order_id": "o_5512", "amount": 649 }

Click a step to see the lines it points at.

Treating logged in as allowed. Authentication proved who they are; authorization decides what they may do. Most "they saw something they should not have" incidents are this exact gap.
Assuming the frontend hiding a button is protection. The customer never saw a refund button, but the endpoint answered a direct call anyway. Hiding UI is not a permission check; the check has to live on the server.
Filing this as "refund bug" without naming the role. The dev needs to know the fix is a role gate returning 403 for non-staff, not a tweak to the refund math itself.
Designing roles only for the happy path. "Admin can edit prices" is easy to remember; "customer must be blocked from refunds and from other customers' orders" is the part that leaks if you skip it.
Confusing 401 and 403. A 401 means the server does not know who you are; a 403 means it knows and refuses. This bug is a missing 403, and using the wrong one in the ticket sends the dev to the wrong code.

Write the ticket as: "A customer-tier token can POST to /api/orders/o_5512/refund and receive 200 refunded. This path must check role before acting and return 403 for anyone who is not staff." You named the endpoint, the wrong outcome, and the exact fix. That is an authorization check, and you just specified one.

Most "they could see something they shouldn't" bugs are authorization failures, and they are serious: they leak data across users.
Role design (who can do what) is a product decision you will own when building any tool with multiple kinds of users.

"A customer could hit the refund endpoint directly, that's an authorization hole."

Appears in Phase 3, Give customers accounts.