auth.role() and auth.email() are deprecated in policies
Older policies use auth.role() = 'authenticated' and auth.email(). How to find them in your database and rewrite them with a TO clause and auth.jwt().
What you see
Policies like these, copied from older tutorials or brought over from another project:
using (auth.role() = 'authenticated')
using (auth.email() = invitee_email)
They still run, but both functions are deprecated and kept only for older projects.
Why it happens
auth.role() reads the role claim from the request's JWT. Postgres already switches to that role for each request, and a policy's to clause can target it directly. That's clearer, and Postgres skips the policy entirely for other roles instead of evaluating a function on every row.
auth.email() reads the email claim. auth.jwt() ->> 'email' does the same and is the supported way to read any claim.
How to fix it
Find the affected policies in Studio's SQL editor:
select schemaname, tablename, policyname, roles, qual, with_check from pg_policies where coalesce(qual, '') || coalesce(with_check, '') ~ 'auth\.(role|email)\(\)';Replace
auth.role() = 'authenticated'with atoclause.alter policycan change the roles and the expression in place:-- Before: for select using (auth.role() = 'authenticated') alter policy "Members can read channels" on public.channels to authenticated using (true);Where the old check was combined with another condition, keep the condition:
-- Before: using (auth.role() = 'authenticated' and auth.uid() = owner_id) alter policy "Owners can update channels" on public.channels to authenticated using ((select auth.uid()) = owner_id);Replace
auth.email()withauth.jwt():alter policy "Invitees can see their invite" on public.invites to authenticated using (invitee_email = (select auth.jwt() ->> 'email'));Wrapping the call in
(select ...)lets Postgres evaluate it once per query instead of once per row. Where you can, match on user ids rather than email addresses, because users can change their email.Drop checks for
service_role. A policy withauth.role() = 'service_role'does nothing useful: theservice_rolekey bypasses Row Level Security anyway.Watch for
anon.auth.role() = 'anon'becomesto anon. If a policy should apply to visitors and signed-in users alike, useto anon, authenticated.Test as each role. Use your app signed out and signed in, and check that reads and writes behave as before.
On SupaHost
The MCP get_advisors tool checks for tables without RLS, tables with RLS but no policies, and functions with a mutable search_path. It doesn't flag deprecated functions in policies, so use the query in step 1.