From 19188ece584b998663dca7fe7a7505c8fa0d6989 Mon Sep 17 00:00:00 2001 From: Miranda Limonczenko Date: Wed, 30 Sep 2026 14:36:05 -0700 Subject: [PATCH] docs(functions): style pass on the Edge Function auth guide (#50884) Apply the docs style guide to Securing Edge Functions. Inline changes only. - Open the page with a value statement - Split the sentences that ran past the 26-word aim, and keep one relationship per sentence - Replace dash-bounded asides with separate sentences - Lift `(the default)` out of parentheses so it reads as a claim - Name the section instead of "above" and "the sections below" - Introduce the mode table in the sentence before it - Raise the `auth: 'none'` admonition to `danger`, and state it in the positive form - Stop restating that admonition in the Public functions section - Spell out Row Level Security, and name `@supabase/server` rather than "the SDK" - Use Supabase Dashboard and Supabase Platform consistently - Use "function" rather than "endpoint", and spell out "db" - Link `@supabase/server` once, and name it as a GitHub destination - Rewrite the Secret keys alt text to describe both rows, the column headers, and the masked key format --- apps/docs/content/guides/functions/auth.mdx | 36 +++++++++++---------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/docs/content/guides/functions/auth.mdx b/apps/docs/content/guides/functions/auth.mdx index 3b9dc9b473f..511dfe9ce9a 100644 --- a/apps/docs/content/guides/functions/auth.mdx +++ b/apps/docs/content/guides/functions/auth.mdx @@ -5,9 +5,11 @@ description: 'Authentication patterns for Supabase Edge Functions.' subtitle: 'Authentication patterns for Edge Functions' --- -The `withSupabase` wrapper from [`@supabase/server`](https://github.com/supabase/server) verifies the caller's credentials against a declared `auth` mode and hands you a pre-configured Supabase client on `ctx`. The sections below show how to use it for each common auth scenario. +Secure an Edge Function by declaring which credentials it accepts. The `withSupabase` wrapper from the [`@supabase/server` package](https://github.com/supabase/server) on GitHub checks each caller against the `auth` mode you set. Your handler receives a preconfigured Supabase client on `ctx`. -For how authorization headers and the `verify_jwt` platform check work under the hood, see [Authorization headers](/docs/guides/functions/auth-headers). +For how authorization headers and the `verify_jwt` platform check work, see [Authorization headers](/docs/guides/functions/auth-headers). + +The wrapper accepts four `auth` modes: | Mode | Accepts | | --------------- | ------------------------------------------ | @@ -18,7 +20,7 @@ For how authorization headers and the `verify_jwt` platform check work under the ## Authenticated user calls -Functions called by signed-in users — typically through `supabase.functions.invoke` from the client — send the user's session JWT on the `Authorization` header. Keep `verify_jwt = true` (the default) so the platform validates the JWT before your handler runs, then use `auth: 'user'` to get `ctx.supabase` already scoped to the caller's RLS policies. +When a signed-in user calls a function, the request carries the user's session JWT on the `Authorization` header. Your app usually makes that call through `supabase.functions.invoke`. The default is `verify_jwt = true`. The platform validates the JWT before your handler runs. Use `auth: 'user'` to get a `ctx.supabase` scoped to the caller's Row Level Security (RLS) policies. ```ts import { withSupabase } from 'npm:@supabase/server@1' @@ -40,7 +42,7 @@ export default { ## Service-to-service calls -Cron jobs, workers, `pg_net`, or another Edge Function make calls with a secret key on the `apikey` header rather than a user JWT. Disable `verify_jwt` and use `auth: 'secret'` to validate the key against any secret key from your [dashboard](/dashboard/project/_/settings/api-keys). You get `ctx.supabaseAdmin` for privileged work. +Cron jobs, workers, `pg_net`, and other Edge Functions make calls with a secret key on the `apikey` header rather than a user JWT. Disable `verify_jwt` and use `auth: 'secret'`. The wrapper validates the key against any secret key in your [project's API keys](/dashboard/project/_/settings/api-keys), and gives your handler `ctx.supabaseAdmin` for privileged work. ```ts import { withSupabase } from 'npm:@supabase/server@1' @@ -55,15 +57,15 @@ export default { -To accept only one specific key, use `auth: 'secret:'`. For example, `auth: 'secret:automations'` only accepts the secret key you named "automations" in the [**Settings > API keys**](/dashboard/project/_/settings/api-keys) section of the Dashboard. The same syntax works for publishable keys (`auth: 'publishable:'`). +To accept only one specific key, use `auth: 'secret:'`. For example, `auth: 'secret:automations'` accepts only the secret key named `automations`. To name a key, open [**Settings > API keys**](/dashboard/project/_/settings/api-keys) in the Supabase Dashboard. The same syntax works for publishable keys: `auth: 'publishable:'`. -![A secret key named "automations" listed under Secret keys in the Supabase dashboard.](/docs/img/guides/functions/secret-keys-automations.png) +![The Secret keys section of the Supabase Dashboard. A table with Name and API key columns lists two keys, "default" and "automations". Each row shows a masked sb_secret_ value with reveal and copy buttons.](/docs/img/guides/functions/secret-keys-automations.png) ## Public functions -For a genuinely public function, like a health check, use `auth: 'none'` with `verify_jwt = false` so anonymous callers can reach the handler. +For a genuinely public function, such as a health check, use `auth: 'none'` with `verify_jwt = false` so anonymous callers can reach the handler. ```toml [functions.health] @@ -81,11 +83,11 @@ export default { } ``` -`auth: 'none'` skips every credential check — see the caution under [External webhooks](#external-webhooks) before using it on anything that reads or writes sensitive data. +`auth: 'none'` accepts every caller. For a function that authenticates callers itself, see the [External webhooks](#external-webhooks) section of this page. ## External webhooks -External providers like Stripe or GitHub don't send Supabase credentials. They sign the request body with their own shared secret. Use `auth: 'none'` to skip the SDK's credential check, then verify the provider's signature inside the handler. Keep `verify_jwt = false`. +External providers such as Stripe or GitHub don't send Supabase credentials. They sign the request body with their own shared secret. Use `auth: 'none'` to skip the wrapper's credential check, then verify the provider's signature inside the handler. Keep `verify_jwt = false`. ```ts import { withSupabase } from 'npm:@supabase/server@1' @@ -117,21 +119,21 @@ export default { return new Response('bad signature', { status: 400 }) } - // your business logic. ctx.supabaseAdmin available for db work + // your business logic. ctx.supabaseAdmin available for database work return Response.json({ received: true }) }), } ``` - + -`auth: 'none'` disables every credential check. Your handler is fully responsible for authenticating the caller. Never use it on an endpoint that reads or writes sensitive data without verifying the caller some other way. +`auth: 'none'` disables every credential check, so your handler is fully responsible for authenticating the caller. When the function reads or writes sensitive data, verify the caller yourself inside the handler. ## Combining modes -Functions that answer both users and internal callers take an array on `auth`. Modes are tried in order. The first match wins, and `ctx.authMode` tells you which matched. +Functions that answer both users and internal callers take an array on `auth`. The wrapper tries each mode in order and uses the first one that matches. `ctx.authMode` tells you which mode matched. ```ts import { withSupabase } from 'npm:@supabase/server@1' @@ -151,7 +153,7 @@ export default { ## Custom error responses -To shape the 401 response yourself, use `createSupabaseContext` instead of `withSupabase`. It returns a `{ data, error }` tuple so you stay in control. +To shape the 401 response yourself, use `createSupabaseContext` instead of `withSupabase`. It returns a `{ data, error }` tuple instead of rejecting the request for you. ```ts import { createSupabaseContext } from 'npm:@supabase/server@1' @@ -169,7 +171,7 @@ export default { ## Environment variables -`@supabase/server` reads its configuration from a standard set of environment variables. On the Supabase platform and in local development with the CLI, these are auto-provisioned. +`@supabase/server` reads its configuration from a standard set of environment variables, which the Supabase Platform and the Supabase CLI provision for you. | Variable | What it is | | --------------------------- | ----------------------------------------- | @@ -178,10 +180,10 @@ export default { | `SUPABASE_SECRET_KEYS` | Named secret keys as a JSON object | | `SUPABASE_JWKS` | JSON Web Key Set used to verify user JWTs | -Local development with the CLI uses a single-key setup, which the SDK also accepts as a fallback: `SUPABASE_PUBLISHABLE_KEY` and `SUPABASE_SECRET_KEY`. +Local development with the CLI uses a single-key setup. `@supabase/server` also accepts `SUPABASE_PUBLISHABLE_KEY` and `SUPABASE_SECRET_KEY` as a fallback. -The same zero-config experience is available on other runtimes. Install [`@supabase/server`](https://github.com/supabase/server) in your Node.js, Bun, Cloudflare Workers, or self-hosted Deno app and set the environment variables above. See the package's [environment variables guide](https://github.com/supabase/server/blob/main/docs/environment-variables.md) for the full reference. +`@supabase/server` configures itself the same way on other runtimes. Install it in your Node.js, Bun, Cloudflare Workers, or self-hosted Deno app, then set the same variables. For the full reference, see the [environment variables guide](https://github.com/supabase/server/blob/main/docs/environment-variables.md) in the `@supabase/server` repository.