How can we manage a dedicated SECURITY DEFINER function owner in Supabase-managed PostgreSQL? #51472
Replies: 3 comments
|
Two things in the catalog row you posted change the picture a bit. First, your membership row has admin_option = true. In Postgres, holding admin option on a role means you can grant and revoke membership in that role, regardless of who the original grantor was. So before treating the membership itself as platform locked, it is worth testing a plain REVOKE of postgres from r07_p1_executor in your dev project. If that works, question 1 mostly goes away. The harder blocker is set_option = false. Owning objects as another role requires being able to SET ROLE into it, and with SET false your postgres session cannot do that, so you cannot create the function with r07_p1_executor as owner. Since you hold admin option, you can also test re-granting your own membership with SET set to true. I cannot say whether the managed platform lets that stick, but it is a quick test in dev. If neither change takes, both are small and specific asks for Supabase support: flip SET on that one membership, or adjust the membership for you. Roles granted by supabase_admin are platform managed, so support is the supported path there. On question 2, the pattern most Supabase projects actually use skips the dedicated owner role:
That gets you nearly all of the least privilege benefit. The separate NOLOGIN owner is cleaner on paper, but on a managed platform its usability depends on exactly the role options you are stuck on, so it is only worth it if support will set it up for you once. For question 4, the main thing to respect is that a definer function runs with its owner's privileges. If that owner is postgres, treat argument handling and any dynamic SQL in exposed functions carefully, and recheck execute grants whenever you add one. In practice that matters more than whether the owner is a separate role. |
|
Building on @aniketatgithub's analysis — the catalog row confirms you're closer to unblocked than it seems, and there's one additional runtime failure mode worth planning for. 1. The two test statements, in order Since your membership row has revoke r07_p1_executor from postgres;You still need grant r07_p1_executor to postgres with set true;That flag is the actual blocker in your row ( 2. The failure mode after ownership transfer: the executor role needs its own table GRANTs
grant select, insert on public.authorization_decisions to r07_p1_executor;
-- nothing beyond what the function actually reads/writesSame discipline the October 30 default-privileges change makes mandatory for the platform roles — if you're running an Oct 30 readiness pass on this project anyway, include SECURITY DEFINER owner roles in the grants inventory so they don't get missed. 3. On question 4 — constraints worth knowing before proceeding
If the re-grant with SET TRUE holds, the full sequence is: grant executor its table minimum → transfer function ownership → revoke/limit postgres membership → leave only |
|
I reproduced your membership row on a local Postgres 17.2, with the bootstrap superuser named That is what Postgres itself records since version 16: when a non-superuser creates a role with Removing it. What the row allows. With only that row, Making the executor the owner. This worked: -- adds a second membership row, granted by postgres; SET is enough for the owner change
grant r07_p1_executor to postgres with set true, inherit false;
-- the new owner also needs CREATE on the function's schema
grant create on schema public to r07_p1_executor;
alter function public.f() owner to r07_p1_executor;
revoke create on schema public from r07_p1_executor;
-- optional: drop the SET grant again; the function stays owned by the executor
revoke r07_p1_executor from postgres;Without the schema grant, Changing the function later. After the last Roles you create later. All of this is vanilla Postgres 17.2. I haven't run it on a hosted project, so try it in your dev project first. |
Uh oh!
There was an error while loading. Please reload this page.
Hi Supabase community,
I'm implementing a server-mediated authorization system in a Supabase project and have encountered a PostgreSQL role-membership limitation. I'd appreciate guidance on the supported approach for managing a dedicated, non-login role that owns a narrowly scoped SECURITY DEFINER function.
Environment
Supabase project: employment-road-dev (DEV environment)
PostgreSQL project ref: emukjkjntxkjbxlrcnjn
PostgreSQL role: r07_p1_executor
The executor role exists with these attributes:
NOLOGIN
NOSUPERUSER
NOCREATEDB
NOCREATEROLE
NOBYPASSRLS
NOINHERIT
Observed role membership
The database catalog reports:
Property | Value -- | -- Member | postgres Granted role | r07_p1_executor Grantor | supabase_admin admin_option | true set_option | false inherit_option | falseThe SQL session available to us reports current_user = postgres and rolsuper = false.
The session cannot revoke this membership because the grant was made by supabase_admin. The membership also does not currently permit postgres to inherit or SET ROLE into r07_p1_executor.
Intended architecture
We want to implement an authenticated, self-authorizing RPC that:
Derives the human actor from auth.uid() rather than accepting a caller-supplied actor ID.
Performs complete authorization checks inside the RPC.
Uses a narrowly scoped SECURITY DEFINER function owned by a dedicated, non-login executor role.
Uses a fixed minimal search_path and schema-qualified references.
Denies direct table CRUD to ordinary application roles.
Performs authorization mutation, idempotency/replay handling, and success-audit insertion atomically.
Keeps exceptional bootstrap and recovery operations separate from the normal authorization path.
We do not want to use service_role as a substitute for human identity or weaken the authorization boundary to work around a platform limitation.
Questions
Is there a supported way in Supabase-managed PostgreSQL to remove or change this specific role membership when the grantor is supabase_admin but the available postgres session is not a superuser?
Is there a documented, recommended pattern for assigning a dedicated non-login role as the owner of a narrowly scoped SECURITY DEFINER function in Supabase?
If the membership cannot be removed by a project owner, what is the supported alternative that preserves least privilege and prevents the normal application identity from assuming or administering the executor role?
Are there specific Supabase role-management or function-security constraints we should account for before proceeding?
We are not requesting broad superuser access. We want to use the supported managed-platform mechanism and preserve the restricted executor role attributes.
No Production changes are involved. The issue is currently blocking implementation in our DEV environment.
Thank you for any guidance or references to relevant documentation.
All reactions