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)returnsnullwhen 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.