DEV Community

Auth By Example
Auth By Example

Posted on

Counts and totals need the same authorization filter as the list

You locked down GET /invoices so it only returns rows the caller can see. Then the dashboard says "Invoices this month: 4,812", and that number comes from SELECT count(*) FROM invoices with no tenant filter.

Counts leak. So do sums, search facets like "Overdue (37)", and the "20 of 3,104" line under a paginated table. When those run on their own query path they can tell a user how large another tenant's account is, or whether a given customer exists at all, without returning a single row.

What I'd do:

  • Build the authorization filter in one place and pass it to the list query, the count query and every aggregate.
  • Check search facets and pagination totals separately. They often come from a different index call than the hits.
  • Add a test: a user who can see 3 invoices gets 3 from the count endpoint, and the facet numbers add up to 3.
  • If dashboard numbers are cached, put the tenant (or the user, when access is per user) in the cache key.

Top comments (0)