Skip to content

SERVICE_ROLE_KEY silently falls back to SUPABASE_KEY — privilege boundary erased #3819

Description

@juliag106

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions