In backend/consts/const.py:74:
SUPABASE_URL = os.getenv('SUPABASE_URL')
SUPABASE_KEY = os.getenv('SUPABASE_KEY')
SERVICE_ROLE_KEY = os.getenv('SERVICE_ROLE_KEY', SUPABASE_KEY)
The fallback is dangerous in either direction:
- If
SERVICE_ROLE_KEY is unset (a common config mistake), the anon key is used for admin operations — meaning everything that calls get_supabase_admin_client() (backend/utils/auth_utils.py:240) will silently fall back to anon-level access. RLS-protected operations will mysteriously fail, often with cryptic 401s buried inside service code.
- If only
SERVICE_ROLE_KEY is set (e.g. a developer setting up locally and not realising they need both), SUPABASE_KEY is None, but SERVICE_ROLE_KEY is now anchored to the actually-set value. Then get_supabase_client() (line 232) tries to create a client with key=None and create_client raises.
The two keys carry fundamentally different privileges in Supabase — the anon key is intended to be public-readable, the service role key bypasses RLS. Conflating them via a silent default is exactly the kind of mistake that's easy to ship and hard to find in production.
Suggested fix
SUPABASE_KEY = os.getenv('SUPABASE_KEY')
SERVICE_ROLE_KEY = os.getenv('SERVICE_ROLE_KEY')
if SERVICE_ROLE_KEY is None:
# Don't silently fall back to the anon key — that erases the privilege boundary.
# In speed/demo mode it's fine to be unset; admin clients will refuse to construct.
logger.warning("SERVICE_ROLE_KEY is not configured; admin-scoped Supabase operations will fail")
…and have get_supabase_admin_client raise a clear RuntimeError (or domain exception) when called with SERVICE_ROLE_KEY is None, instead of create_client failing inside a generic except.
Category: H (security hardening). Severity: High.
In
backend/consts/const.py:74:The fallback is dangerous in either direction:
SERVICE_ROLE_KEYis unset (a common config mistake), the anon key is used for admin operations — meaning everything that callsget_supabase_admin_client()(backend/utils/auth_utils.py:240) will silently fall back to anon-level access. RLS-protected operations will mysteriously fail, often with cryptic 401s buried inside service code.SERVICE_ROLE_KEYis set (e.g. a developer setting up locally and not realising they need both),SUPABASE_KEYisNone, butSERVICE_ROLE_KEYis now anchored to the actually-set value. Thenget_supabase_client()(line 232) tries to create a client withkey=Noneandcreate_clientraises.The two keys carry fundamentally different privileges in Supabase — the anon key is intended to be public-readable, the service role key bypasses RLS. Conflating them via a silent default is exactly the kind of mistake that's easy to ship and hard to find in production.
Suggested fix
…and have
get_supabase_admin_clientraise a clearRuntimeError(or domain exception) when called withSERVICE_ROLE_KEY is None, instead ofcreate_clientfailing inside a generic except.Category: H (security hardening). Severity: High.