All guidesSupabase RLS and keys
On this page

Supabase Security Advisor

Supabase

What does authenticated_security_definer_function_executable mean, and how do I fix it?

By Ohad Krispin, founder of VibeRaven · LinkedIn

authenticated_security_definer_function_executable is the Supabase Security Advisor warning (lint 0029, level WARN) for a security definer function in an exposed schema that the authenticated role can execute. Any signed-in user, and with open sign-up that means anyone with an email address, can call it at /rest/v1/rpc/<name>, and it runs with its owner's rights, so row level security does not limit what it reads or writes. For a function only your server calls, the fix is to revoke EXECUTE from authenticated, together with anon and public.

psql output from a Supabase Postgres 17.11 container: after a revoke from public and anon, lint 0029 still flags all_todos and a signed-in user reads another user's todo. After a revoke from authenticated, the call is denied.
psql output from a Supabase Postgres 17.11 container: after a revoke from public and anon, lint 0029 still flags all_todos and a signed-in user reads another user's todo. After a revoke from authenticated, the call is denied.

Checklist

How to fix authenticated_security_definer_function_executable, then check the fix

  1. 01
    List the security definer functions in public and who can call them. A function you already closed to anon often still shows true for signed-in users, because Supabase grants EXECUTE on new functions in public to anon and authenticated separately from the Postgres default to public:
    select
        p.oid::regprocedure as function,
        pg_catalog.pg_get_userbyid(p.proowner) as owner,
        has_function_privilege('anon', p.oid, 'execute') as anon_can_call,
        has_function_privilege('authenticated', p.oid, 'execute') as signed_in_can_call
    from pg_catalog.pg_proc p
    join pg_catalog.pg_namespace n on n.oid = p.pronamespace
    where n.nspname = 'public'
      and p.prosecdef
    order by 1;
  2. 02
    Only your server calls it: revoke all three in one statement, in a migration.
    revoke execute on function public.all_todos() from public, anon, authenticated;
  3. 03
    It does not need to bypass RLS: make it security invoker, so the caller's policies apply. On the test container this also cleared the lint.
    alter function public.all_todos() security invoker;
  4. 04
    A trigger function, such as one that creates a profile row at sign-up, is flagged too, but it cannot be called at /rest/v1/rpc: Postgres says trigger functions can only be called as triggers. Revoking is safe. On a Supabase Postgres 17.11 container on 22 October 2026, a trigger kept firing after its function was revoked from all three roles.
    revoke execute on function public.handle_new_user() from public, anon, authenticated;
  5. 05Run the Security Advisor again in the dashboard. Supabase CLI 2.121.0 db advisors, run against a local database, did not report lint 0028 or 0029 at all, so it cannot confirm this fix.
  6. 06Call the function as a signed-in user. On the container, after a revoke from public and anon, a signed-in user got their own todo and another user's through all_todos(); after the revoke from authenticated, the call failed with permission denied for function all_todos.

Exceptions

When signed-in users should call it

The lint's source flags every security definer function that authenticated can execute in a schema the Data API exposes, including trigger functions. A function in a private schema is not flagged.

A per-user privileged operation

Supabase's lint docs name a "create my profile" or "submit feedback" function as intentional. Close it to anon, keep it for authenticated, and limit it to the caller inside. On the container, this returned 1 to a signed-in user and permission denied to anon. The Advisor keeps listing it.

create function public.my_todo_count()
returns bigint
language sql
stable
security definer
set search_path = ''
as $$
  select count(*) from public.todos where user_id = (select auth.uid())
$$;

revoke execute on function public.my_todo_count() from public, anon;
grant execute on function public.my_todo_count() to authenticated;

An owner with no extra rights

If the function's owner can do no more than the caller, security definer gains nothing. The owner column above shows it; on Supabase it is usually postgres, which bypasses RLS.

Related

Sources

Official docs behind this checklist

FAQ

FAQ for builders before launch

How is it different from anon_security_definer_function_executable?

Lint 0028 is the same check for the anon role, so anyone holding your publishable key. Lint 0029 is for signed-in users. Supabase's lint docs say to fix them together, because revoking from one role usually leaves the other able to call it.

Does turning off pg_graphql fix it?

No. Supabase's lint docs say it closes the /graphql/v1 route only; /rest/v1/rpc still runs the function, and the lint keeps firing.