Why letting clients send their own filters breaks API security (OWASP API1/BOLA), and how to enforce entitlements in GraphQL.

Opening an API to external customers raised a question that sounds like a detail and isn't: "the client already knows what it's entitled to — can't it just send its own filters?"

No. Anything the client sends is a suggestion, not a constraint.

The pattern

  • The API key maps to a contract record — allowed categories, regions, fields, rate limits — loaded in memory and refreshed on change.
  • The server intersects every request with that contract. A caller asking outside it gets an empty result, not an error that confirms the data exists.
  • Lookups by ID go through the same check, which closes the BOLA hole: item(id: 123) returns null when 123 is outside the contract, even though the row exists.
  • Aggregates are computed over the constrained set — a count over everything leaks how much data the customer doesn't have.

The lesson: authorization is a server-side computation over server-side state. Client-sent filters are for convenience; entitlements are for enforcement.