Can't revoke a role membership granted by supabase_admin
Supabase grants some role memberships as supabase_admin, and the postgres role can’t revoke them. You see one of the following:
revoke my_role from postgrescompletes, but the membership stays.revoke ... granted by supabase_adminfails withpermission denied to revoke privileges granted by role "supabase_admin".drop role my_rolefails withpermission denied to drop role.
Postgres reports the skipped revoke as a warning in your Postgres logs, not as an error. This page helps you find who granted a membership and work with the roles you create.
If a role you created needs access to Auth data, see Permission errors when granting custom roles access to the auth schema.
Overview
In Postgres, a role can revoke only the privileges and memberships that it granted. For more information about grantors and revoking, see the Postgres documentation on REVOKE.
The postgres role isn’t a superuser. It can’t become supabase_admin or the other Supabase administrator roles, because only a superuser can grant membership in them. As a result, postgres can’t act as these roles to change what they granted.
Postgres records the grantor of each role membership. On Postgres 16 or later, when postgres creates a role, Postgres grants postgres an admin membership in the new role. The bootstrap superuser, supabase_admin, is recorded as the grantor. The memberships that Supabase grants to its own roles and to postgres also have supabase_admin as the grantor.
The admin membership in roles you create
Every role you create gets this admin membership, and you can’t remove it. The membership has these properties:
- It lets
postgresdrop the role and grant it to other roles. Without it, only a superuser could do that. - It has
inheritandsetturned off.postgresdoesn’t receive the role’s privileges, and it can runset roleto the role only after it grants itself thesetoption. - Other roles can act as your role only if you grant it to them.
Memberships that Supabase grants
Supabase grants postgres membership in anon, authenticated, service_role, authenticator, pg_read_all_data, pg_monitor, pg_signal_backend, and other roles. The Data API connects as authenticator and switches to anon, authenticated, or service_role for each request, so authenticator is a member of those roles.
When you run the Supabase CLI without a database password, Supabase creates a temporary cli_login_postgres role that is a member of postgres. To remove it, see ‘Permission denied’ when deleting the ‘cli_login_postgres’ role.
Statements that don’t work
revoke admin option for my_role from postgrescompletes without changing anything, becausesupabase_admingranted theadminmembership. If your migrations contain this statement, remove it.grant ... granted by supabase_adminfails withpermission denied to grant privileges as role "supabase_admin".
Diagnose the problem
List who granted a role’s memberships
To list the memberships of a role and who granted each one, replace my_role in the following query.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
select m.roleid::regrole as role, m.member::regrole as member, m.grantor::regrole as grantor, m.admin_option, m.inherit_option, m.set_optionfrom pg_auth_members mwhere m.roleid = 'my_role'::regrole or m.member = 'my_role'::regrole;Find roles that postgres can’t drop or grant
If postgres doesn’t have the admin option on a role, it can’t drop or grant the role. A grant fails with permission denied to grant role "my_role", and drop role my_role fails with:
ERROR: 42501: permission denied to drop roleDETAIL: Only roles with the CREATEROLE attribute and the ADMIN option on role "my_role" may drop this role.The following query lists the roles, other than the Supabase roles, that postgres can’t drop or grant.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
select r.rolnamefrom pg_roles as rwhere r.rolname not like 'pg\_%' and r.rolname not like 'supabase\_%' and r.rolname not like 'pgsodium\_%' and r.rolname not like 'cli\_login\_%' and r.rolname not in ( 'postgres', 'anon', 'authenticated', 'authenticator', 'service_role', 'dashboard_user', 'pgbouncer', 'pgtle_admin' ) and not pg_has_role('postgres', r.oid, 'member with admin option');Read the output
- In the first query, if
grantorissupabase_admin,postgrescan’t revoke that membership. - The second query lists roles that
postgrescan’t drop or grant. You can still stop clients from connecting as them. See Stop clients from connecting as a role.
Solve the problem
Change an object’s owner to a role you create
To change an object’s owner to your role, postgres must be able to run set role to it, and your role needs create on the object’s schema. Otherwise, alter ... owner to fails with must be able to SET ROLE "my_role" or permission denied for schema.
The following SQL grants both for the change only, then removes them.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
grant create on schema private to my_role;grant my_role to postgres with set true, inherit false;alter function private.my_function() owner to my_role;revoke my_role from postgres;revoke create on schema private from my_role;The first revoke removes only the membership that postgres granted. The admin membership granted by supabase_admin stays, with set turned off.
Re-create a membership you removed
To re-create a membership, grant it again with the options it had.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
grant my_role to other_role with admin false, inherit false, set true;Postgres records postgres as the grantor. You can’t record supabase_admin as the grantor. The grantor doesn’t change what the membership allows. It decides which role can revoke the membership.
Stop clients from connecting as a role
postgres can run alter role on any role you create, including roles it can’t drop or grant.
To stop clients from connecting as a role, set it to nologin.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
alter role my_role nologin;Give services their own roles
To limit what a service or person can do, give them their own role with only the grants they need. Keep the postgres credentials for administration.
Confirm the problem is solved
After you change an owner, check what postgres can do with the role.
Run the following SQL using the SQL Editor in the Supabase Dashboard or with psql over a direct connection. To connect with psql, see Direct connection.
select pg_has_role('postgres', 'my_role', 'set') as can_set_role, pg_has_role('postgres', 'my_role', 'usage') as has_privileges;Both values are false after the revoke.
After you re-create a membership, run the query in List who granted a role’s memberships. The grantor of the new membership is postgres.