# Enforcing Authorization in APIs — Authentication & Authorization

Source: https://www.skillbyai.com/en/authentication/z-enforce

> Object-level checks and policy engines.

## Check every object, deny by default

The most common API flaw is **broken object level authorization** (BOLA): `GET /orders/123` returns order 123 to anyone logged in because the handler never checks ownership. Enforce authorization **server-side on every request**, for **every object**, defaulting to **deny**. Prefer scoping queries by the caller (`WHERE id = ? AND account_id = ?`) so unauthorised rows are never loaded, and check **function-level** permissions (admin routes) and **property-level** rules (who may set `role` or `price`). Centralise decisions in one module or a **policy engine** so rules are consistent and testable: **Open Policy Agent (OPA)** evaluates policies written in Rego; **Cedar** is a policy language from AWS used by Amazon Verified Permissions. Engines decide; your code must still call them and act on the answer.

## Scoped queries plus a central policy check

Express-style TypeScript; can() is your policy module or engine client.

```typescript
// central decision point: easy to test and audit
export function can(user: User, action: string, resource: { ownerId: string; accountId: string }) {
  if (user.accountId !== resource.accountId) return false;        // tenant boundary
  if (action === 'order:read') return user.id === resource.ownerId || user.roles.includes('support');
  if (action === 'order:refund') return user.roles.includes('finance');
  return false;                                                    // deny by default
}

app.post('/orders/:id/refund', requireUser, async (req, res) => {
  // scope by tenant in the query itself
  const order = await db.orders.findOne({ id: req.params.id, accountId: req.user.accountId });
  if (!order) return res.status(404).end();
  if (!can(req.user, 'order:refund', order)) return res.status(403).end();
  await refunds.create(order, { amount: order.total }); // amount from server data, not the client
  res.status(202).end();
});
```

## Test authorization like a feature

Write tests that log in as user A and request user B's objects, for every endpoint. Authorization bugs rarely show up in happy-path tests.

**Quiz:** An API returns any invoice by ID to any logged-in user. What is this flaw called?

- [ ] Session fixation
- [x] Broken object level authorization (BOLA / IDOR)
- [ ] Credential stuffing
- [ ] Algorithm confusion

*Answer:* Broken object level authorization (BOLA / IDOR). The handler authenticates but never checks ownership of the object.
