jwt custom claims or a memberships join in every policy at 200 orgs Supabase Auth
Every policy currently does:
using ( org_id in (select org_id from memberships where user_id = (select auth.uid())) )
That adds 6-9ms per query and a dashboard page runs 11 queries, so it is roughly 80ms of pure policy overhead on a page that should be 150ms. 200 organisations, average 8 members.
The alternative is putting org_id and role into the access token as custom claims and comparing against those. My worry is revocation - if someone is removed from an org, their token is still valid until it expires.
@pgpolicy_nadia · 4w ago · 3 replies
Claims are a cache. Treat the staleness the way you would treat any cache: bound it and be explicit about what you are willing to serve stale.
Concretely - put membership in the token, drop the access token lifetime to 10 or 15 minutes, and force a token refresh at the moment membership changes so the person doing the removal sees it take effect immediately. Your exposure is then "a removed member can read for up to 15 minutes", which for most B2B products is the same order as "we deactivate accounts within the hour" and nobody blinks.
Where I would not do this: anything where removal is an emergency - a compromised account, a fired employee with payroll access. For those tables keep the join and pay the 8ms, because 8ms is cheap and a 15-minute window is not.
Reply
Report
@soy_pour_sasha · 3w ago
Splitting it by table is the answer I did not consider. Invoices and member management keep the join, everything read-heavy moves to claims.
Reply
Report
@supa_okonkwo · 4w ago
If you do split, write down the rule somewhere the next person will find it. A mixed policy set where some tables trust the token and some do not is genuinely confusing six months later.
Reply
Report