mirror of
https://github.com/supabase/supabase.git
synced 2026-10-23 12:06:52 +08:00
Found during my dogfooding project :) ## Problem Three guides give conflicting guidance on how Postgres should authenticate when it calls an Edge Function: - **Securing Edge Functions:** calls from `pg_net` and cron jobs send the secret key on `apikey`, with `auth: 'secret'` and `verify_jwt = false`. The Dashboard agrees: the webhook and cron job forms only offer **Add secret key**. - **Scheduling Edge Functions:** the only example that stores a key in Vault sends the publishable key. A request with only the publishable key passes the `verify_jwt` check, so anyone who has that public key can call the function. - **Database Webhooks:** doesn't cover calling an Edge Function or authenticating to it. Its SQL example writes headers inline, so a webhook created in a migration puts the secret key in a committed file. Resolves [DOCS-1458](https://linear.app/supabase/issue/DOCS-1458), [FDBKIN-19992](https://linear.app/supabase/issue/FDBKIN-19992) ## Solution All three pages now recommend the secret key for calls from Postgres. - **Scheduling Edge Functions:** stores and sends the secret key, explains why the publishable key isn't safe here, and links to the function-side setup. - **Database Webhooks:** a new Call an Edge Function section covers the Supabase Dashboard flow and a migration. The migration reads the key from Vault in a `security definer` trigger function. The section explains that the definer is required, because the roles that insert rows can't read Vault. - **Securing Edge Functions:** the service-to-service section is now a two-step setup. It shows how to disable the `verify_jwt` check in `supabase/config.toml`, with a link to the Authorization headers guide, and how to use `auth: 'secret'` in the function. It then links to Database Webhooks and Scheduling Edge Functions. - **`pg_net` queue:** Database Webhooks and Scheduling Edge Functions show a caution that any role that can log in to the database can read the secret key while the request waits, because `pg_net` writes the decrypted key to `net.http_request_queue` until it sends the request. Both pages include the caution from a new partial, `_partials/pg_net_queued_key.mdx`, which links to the troubleshooting article. ## Verification (`/test-the-docs`) | Snippet / step | Class | Sandbox | Result | Notes | | --- | --- | --- | --- | --- | | Function with `auth: 'secret'` and `verify_jwt = false`, called with the secret key on `apikey` | runnable-with-setup | Compose runner + `supabase start` | pass | Returns `200` | | Same function, called with the publishable key, then no key | runnable-with-setup | Compose runner + `supabase start` | pass | Rejected with `INVALID_API_KEY`, then `MISSING_CREDENTIALS` | | Scheduling Edge Functions: Vault secrets, then `cron.schedule` | runnable-with-setup | Compose runner + `supabase start` | pass | The cron run succeeded, and the function returned `200` | | Database Webhooks: Vault-based trigger function and trigger | runnable-with-setup | Compose runner + `supabase start` | pass | Inserts as `postgres` and as `authenticated` both reached the function with the documented payload | | Same trigger function as `security invoker`, insert as `authenticated` | runnable-with-setup | Compose runner + `supabase start` | pass | Fails with `permission denied for schema vault`, which confirms the page's explanation | | Database Webhooks: Dashboard steps | deferred | — | deferred | Needs a hosted project. Labels confirmed in the Studio source. | **Tier A path:** Vault secrets → `pg_cron` job → `pg_net` request → Edge Function with `auth: 'secret'` returns `200`. **Environment:** OrbStack; compose sandbox (`sandbox/run.sh`); fences in-container only. Two deviations from the stock sandbox, neither committed: - The sandbox pins Supabase CLI v2.39.2, which predates `sb_secret_` and `sb_publishable_` keys. The run used v2.121.0. - The `dind` service doesn't mount `/work`, so local functions fail with `failed to determine entrypoint`. The function file was copied into the DinD filesystem. `project_url` pointed at the local API gateway, because the database container can't reach the runner's `127.0.0.1`. ## Preview links - [Database Webhooks](https://docs-git-alexisa-docs-1458-supabase.vercel.app/docs/guides/database/webhooks) - [Scheduling Edge Functions](https://docs-git-alexisa-docs-1458-supabase.vercel.app/docs/guides/functions/schedule-functions) - [Securing Edge Functions](https://docs-git-alexisa-docs-1458-supabase.vercel.app/docs/guides/functions/auth) ## Post-merge > [!IMPORTANT] > After this PR merges, post the following comment on [FDBKIN-19992](https://linear.app/supabase/issue/FDBKIN-19992) and close it: > > The "Missing required headers" error happens when the webhook doesn't send an auth header to the Edge Function. Database Webhooks send only the headers you configure. [Database Webhooks → Call an Edge Function](https://supabase.com/docs/guides/database/webhooks#call-an-edge-function) shows how to add the secret key, either in the Dashboard with **Add secret key** or in a migration that reads the key from Vault. [Securing Edge Functions](https://supabase.com/docs/guides/functions/auth#service-to-service-calls) covers the function side: disable `verify_jwt` and use `auth: 'secret'`.