feat(self-hosted): add api gateway logic for functions (#46810)

This commit is contained in:
Andrey A. authored and GitHub committed 2026-09-30 11:14:47 +02:00
1 parent 904e3c4aa0
commit c8b665caf2
9 files changed
+284 -13

No files matched your search

@@ -326,10 +326,11 @@ To change the database password, read [Changing database password](#changing-dat
## Accessing Edge Functions
Edge Functions live in `volumes/functions`. The default setup includes a `hello` function you can invoke with `curl`:
Edge Functions live in `volumes/functions`. The default setup includes a `hello` function you can invoke with `curl`. It requires your publishable or secret key in the `apikey` header:
```sh
curl http://<your-domain>:8000/functions/v1/hello
curl http://<your-domain>:8000/functions/v1/hello \
--header 'apikey: <SUPABASE_PUBLISHABLE_KEY>'
```
Add new functions at `volumes/functions/<FUNCTION_NAME>/index.ts`, then restart the service to pick them up:
@@ -113,7 +113,7 @@ Routes are matched in the order declared. The first matching prefix wins. Protec
| `/.well-known/oauth-authorization-server` | auth | - | Open | OAuth 2.0 Authorization Server Metadata (RFC 8414) |
| `/auth/v1/sso/saml/acs` | auth | `/sso/saml/acs` | Open | SAML assertion consumer |
| `/auth/v1/sso/saml/metadata` | auth | `/sso/saml/metadata` | Open | SAML metadata |
| `/functions/v1/` | functions | `/` | Bypass | Edge Functions runtime performs its own JWT verification; 150s timeout |
| `/functions/v1/` | functions | `/` | `sb_` keys | Rejects invalid and conflicting `sb_` keys; others pass; timeout: 150s |
| `/storage/v1/` | storage | `/` | Bypass | Storage performs its own authorization |
| `/auth/v1/` | auth | `/` | API key | Protected Auth endpoints |
| `/rest/v1/` | rest | `/` | API key | PostgREST Open API root (requires secret key) |
@@ -154,7 +154,7 @@ A Lua filter rejects missing or invalid keys with HTTP `401 Unauthorized`. An RB
### Opaque key translation
When the new API keys (`sb_publishable_*`, `sb_secret_*`) are configured, a chain of Lua filters translates opaque keys into the corresponding pre-signed internal JWTs before the request reaches API key enforcement and upstream services. **The entire chain is skipped on `/functions/v1/`**: the Edge Runtime receives the original `apikey` and `Authorization` headers unchanged.
When the new API keys (`sb_publishable_*`, `sb_secret_*`) are configured, a chain of Lua filters translates opaque keys into the corresponding pre-signed internal JWTs before the request reaches API key enforcement and upstream services. **The chain is skipped on `/functions/v1/`**, which has its own filter. See [Edge Functions](#edge-functions).
The chain operates in this order:
@@ -166,6 +166,23 @@ The chain operates in this order:
For background on opaque vs asymmetric keys, see [New API Keys and Asymmetric Authentication](/docs/guides/self-hosting/self-hosted-auth-keys).
### Edge Functions
On `/functions/v1/`, a dedicated Lua filter handles API keys without changing the `apikey` or `Authorization` headers. When the new API keys are configured, it works as follows:
| Request | Result |
| ------------------------------------------------------------------ | -------------------------------------------------------- |
| No `apikey` or `Authorization` | Passed through |
| `apikey` is `sb_publishable_*` or `sb_secret_*` | Passed through with `sb-api-key` set to the internal JWT |
| `Authorization: Bearer sb_...` and no `apikey` | Same as above, using the key from `Authorization` |
| Non-`sb_` value (legacy API key, user session token, any other) | Passed through |
| Unknown `sb_` key | Rejected with `401 Unauthorized` |
| `apikey` and `Authorization: Bearer sb_...` contain different keys | Rejected with `401 Unauthorized` |
The filter always removes any `sb-api-key` header sent by the client. Without the new API keys configured, all other requests pass through unchanged. The `sb-api-key` value is the raw JWT, without a `Bearer` prefix. The `apikey` query parameter is not read on this route.
When `FUNCTIONS_VERIFY_JWT` is `true`, the functions service verifies the JWT in `Authorization`. If `Authorization` is missing or contains an `sb_` key, it verifies `sb-api-key` instead. It removes `sb-api-key` before the request reaches your function. Functions can also check API keys themselves, for example with `withSupabase` from `@supabase/server`. See [Self-hosted Edge Functions](/docs/guides/self-hosting/self-hosted-functions).
## Forwarded headers and CORS
### X-Forwarded headers
@@ -42,7 +42,7 @@ Add the following code to `index.ts`:
import { withSupabase } from '@supabase/server'
export default {
fetch: withSupabase({ auth: 'none' }, async (req) => {
fetch: withSupabase({ auth: ['publishable', 'secret'] }, async (req) => {
const { name } = await req.json()
const message = `Hello, ${name}!`
@@ -51,7 +51,7 @@ export default {
}
```
The `auth` option controls who can call the function: `'none'` accepts every request, `'user'` requires a valid user JWT, and `'publishable'` / `'secret'` require an API key. See the [Edge Functions auth guide](/docs/guides/functions/auth) for details.
The `auth` option controls who can call the function. This example matches the default `hello` function and requires a publishable or secret API key in the `apikey` header. `'none'` accepts every request, and `'user'` requires a valid user JWT. See the [Edge Functions auth guide](/docs/guides/functions/auth) for details.
### Step 2: Restart the functions service to pick up the new function
@@ -63,6 +63,7 @@ sh run.sh restart functions
```sh
curl -X POST http://<your-domain>/functions/v1/my-function \
-H 'apikey: <SUPABASE_PUBLISHABLE_KEY>' \
-H 'Content-Type: application/json' \
-d '{"name": "World"}'
```
@@ -226,7 +227,8 @@ Common causes: syntax errors in your function code, invalid imports, or missing
### 401 "invalid JWT"
- Check that `FUNCTIONS_VERIFY_JWT` matches your intent (`true` or `false`) in `.env`
- If verification is enabled, ensure you're passing a valid token: `Authorization: Bearer <anon_key or service_role_key>`
- If verification is enabled, pass either a valid JWT in `Authorization: Bearer <token>` (a user session token or a legacy API key), or an `sb_publishable_*` or `sb_secret_*` key in the `apikey` header. The API gateway translates `sb_` keys to an internal JWT, which the functions service then verifies.
- Verifying asymmetric JWTs, including translated `sb_` keys, requires `SUPABASE_JWKS` to be set for the `functions` service in `docker-compose.yml`. See [New API Keys and Asymmetric Authentication](/docs/guides/self-hosting/self-hosted-auth-keys).
### Changes to function code not reflected after editing