Row-Level Security in production
Three RLS patterns covering 95% of use cases: ownership, multi-tenant, hierarchical org. With pg_prove and debug tests.
What is RLS
Row-Level Security is a native PostgreSQL feature that filters returned (or accepted) rows on the fly, by evaluating an SQL expression that may depend on the current role and JWT claims. Aurabase injects user claims via request.jwt.claims and exposes helpers auth.uid(), auth.role(), auth.tenant(), auth.has_role().
Ownership — each user their own lines
Use cases: profiles, notes, posts where each line belongs to a unique user via user_id or author_id.
Combine for all with using + with check to cover SELECT/UPDATE/DELETE in one policy. The INSERT only uses with check.
Multi-tenant — scope by organization
Use case: B2B SaaS where each user belongs to an organization, and sees all the data of their org. The JWT contains tenant, the auth.tenant() helper returns it.
Hierarchical org — permissions by role
Use case: SaaS where the roles in an org (admin, member, viewer) have different permissions. We combine auth.tenant() with auth.has_role().
pg_prove — one assertion per policy
Policies are code. We test them. pgTAP + pg_prove run in CI on each migration.
When the policy doesn't do what you want
- RLS disabled? Check with
select relrowsecurity from pg_class where relname = 'posts' - Policies which add up: multiple policies on the same table + operation + role combine in OR. Restrict with AND in a single policy if necessary
- Missing GRANT permissions: even with a policy, the role must have
grant select on posts to authenticated - Claims not injected: from Postgres,
select current_setting('request.jwt.claims', true)— null means you are in direct DB, not via the gateway