TL;DR: Externalized authorization pushes access decisions out of application code and into a policy engine, then uses query planning to filter data at the database layer instead of row-by-row in the app, according to Cerbos. That pattern reduces waste, but it also makes policy expressiveness, fallback post-filtering, and query planning discipline core IAM design concerns.
NHIMG editorial — based on content published by Cerbos: externalized authorization in Convex and query plan filtering
Questions worth separating out
A: Security teams should centralise policy in a dedicated engine, then translate the resulting decision or query plan into database-native filtering wherever possible.
Q: Why do query plans improve authorization performance for data-heavy applications?
A: Query plans let the authorization engine describe the conditions for access once, then let the database apply those conditions using its own indexes and execution engine.
Q: Where does externalized authorization fail in practice?
A: It fails when teams treat the policy engine as separate from data access and allow broad post-filtering or duplicate logic in the application.
Practitioner guidance
- Map policy attributes to queryable fields first Align principal, resource, and action attributes with database columns or filterable properties before writing policies so the query layer can enforce access without broad client-side reads.
- Minimise post-filter usage for sensitive datasets Review any policy that depends on unsupported string or collection operators and decide whether the exposure created by reading data before full evaluation is acceptable.
- Separate policy evaluation from application logic Remove duplicate authorization checks from code paths and make the policy engine the single source of truth for permit, deny, and query-plan generation.
What's in the full article
Cerbos' full article covers the implementation detail this post intentionally leaves for the source:
- Concrete code examples for queryPlanToConvex, including how filter and postFilter are returned in practice
- Operator-by-operator breakdown of what Convex can push to the database and what must be evaluated in JavaScript
- Working policy and query snippets showing how attribute mapping drives query planning
- Guidance on when allowPostFilter should be enabled and what the safety trade-off looks like
👉 Read Cerbos' guide to externalized authorization and Convex query plans →
Externalized authorization in Convex: are your filters in the right place?
Explore further
Externalized authorization is really a control-placement decision. When policy is separated from application code, the central question becomes where access is enforced, not whether access is enforced at all. If the database can apply the policy directly, the application stops acting as the filter layer and the blast radius of overfetching shrinks. For practitioners, the lesson is that enforcement locality is now part of IAM design, not a backend implementation detail.
A few things that frame the scale:
- Organisations maintain an average of 6 distinct secrets manager instances, creating fragmentation that undermines centralised control, according to The State of Secrets in AppSec.
- Only 44% of developers are reported to follow security best practices for secrets management, exposing a significant developer behaviour gap, according to The State of Secrets in AppSec.
A question worth separating out:
Q: What is the difference between database pushdown and post-filtering in authorization?
A: Database pushdown means the database enforces the access condition directly as part of the query. Post-filtering means the application retrieves records first and then removes unauthorized rows in code. Pushdown is stronger for governance and scale because it keeps unauthorised data out of the application path whenever the policy language and database can express the rule.
👉 Read our full editorial: Externalized authorization in Convex shifts filtering to the data layer