diff --git a/apps/docs/content/guides/self-hosting/self-hosted-auth-keys.mdx b/apps/docs/content/guides/self-hosting/self-hosted-auth-keys.mdx index 221bf5cb80d..5101014c539 100644 --- a/apps/docs/content/guides/self-hosting/self-hosted-auth-keys.mdx +++ b/apps/docs/content/guides/self-hosting/self-hosted-auth-keys.mdx @@ -82,14 +82,14 @@ sb_secret_<22-char-random>_<8-char-checksum> ### Verifying the setup -Test with the new publishable key: +Test with the new secret key: ```sh curl http:///rest/v1/ \ -H "apikey: your-supabase-secret-key" ``` -You should receive a valid response from PostgREST. Then verify that the legacy key still works: +You should receive a valid response from PostgREST. Then verify that the legacy service role key still works: ```sh curl http:///rest/v1/ \ @@ -98,6 +98,12 @@ curl http:///rest/v1/ \ Both should work and return the same result. + + +`/rest/v1/` is the Open API root and requires admin-level keys (`sb_secret_*` or legacy `SERVICE_ROLE_KEY`). Public keys (`sb_publishable_*` or legacy `ANON_KEY`) return `403` for this endpoint. + + + You can also verify the public JWKS endpoint: ```sh diff --git a/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx b/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx index fc9cb46fa48..592bc395ed2 100644 --- a/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx +++ b/apps/docs/content/guides/self-hosting/self-hosted-envoy.mdx @@ -127,28 +127,29 @@ docker compose -f docker-compose.yml -f docker-compose.envoy.yml restart api-gw Routes are matched in the order declared. The first matching prefix wins. Protected routes require a valid `apikey` header; open routes pass through without API key validation. -| Path prefix | Upstream | Path rewrite | Access control | Notes | -| ----------------------------------------- | --------- | ------------------------ | ----------------- | ---------------------------------------------------------------------- | -| `/auth/v1/verify` | auth | `/verify` | Open | Email verification | -| `/auth/v1/callback` | auth | `/callback` | Open | OAuth callback | -| `/auth/v1/authorize` | auth | `/authorize` | Open | OAuth authorize | -| `/auth/v1/.well-known/jwks.json` | auth | `/.well-known/jwks.json` | Open | JWKS for third-party verification | -| `/.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 | -| `/storage/v1/` | storage | `/` | Bypass | Storage performs its own authorization | -| `/auth/v1/` | auth | `/` | API key | Protected Auth endpoints | -| `/rest/v1/` | rest | `/` | API key | PostgREST | -| `/graphql/v1` | rest | `/rpc/graphql` | API key | pg_graphql (adds `Content-Profile: graphql_public`) | -| `/realtime/v1/api/tenants` | realtime | - | Denied | Realtime management API (blocked by default) | -| `/realtime/v1/api/openapi` | realtime | - | Denied | Realtime OpenAPI spec (blocked by default) | -| `/realtime/v1/api` | realtime | `/api` | API key | Realtime REST API (broadcast, channels, ping) | -| `/realtime/v1/` | realtime | `/socket/` | API key | Realtime WebSocket | -| `/pg/` | meta | `/` | Service role only | postgres-meta - used by Studio for database access | -| `/api/mcp` | studio | - | Denied | MCP endpoint (blocked by default via RBAC DENY) | -| `/mcp` | studio | `/api/mcp` | Denied | MCP endpoint (blocked by default via RBAC DENY) | -| `/` (catch-all) | studio | - | Basic auth | Dashboard; strips inbound `Authorization` header | +| Path prefix | Upstream | Path rewrite | Access control | Notes | +| ----------------------------------------- | --------- | ------------------------ | -------------- | ---------------------------------------------------------------------- | +| `/auth/v1/verify` | auth | `/verify` | Open | Email verification | +| `/auth/v1/callback` | auth | `/callback` | Open | OAuth callback | +| `/auth/v1/authorize` | auth | `/authorize` | Open | OAuth authorize | +| `/auth/v1/.well-known/jwks.json` | auth | `/.well-known/jwks.json` | Open | JWKS for third-party verification | +| `/.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 | +| `/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) | +| `/rest/v1/*` | rest | `/` | API key | PostgREST data endpoints | +| `/graphql/v1` | rest | `/rpc/graphql` | API key | pg_graphql (adds `Content-Profile: graphql_public`) | +| `/realtime/v1/api/tenants` | realtime | - | Denied | Realtime management API (blocked by default) | +| `/realtime/v1/api/openapi` | realtime | - | Denied | Realtime OpenAPI spec (blocked by default) | +| `/realtime/v1/api` | realtime | `/api` | API key | Realtime REST API (broadcast, channels, ping) | +| `/realtime/v1/` | realtime | `/socket/` | API key | Realtime WebSocket | +| `/pg/` | meta | `/` | API key | postgres-meta / Studio database operations (requires secret key) | +| `/api/mcp` | studio | - | Denied | MCP endpoint (blocked by default via RBAC DENY) | +| `/mcp` | studio | `/api/mcp` | Denied | MCP endpoint (blocked by default via RBAC DENY) | +| `/` (catch-all) | studio | - | Basic auth | Dashboard; strips inbound `Authorization` header | @@ -170,8 +171,9 @@ The protected routes (`/auth/v1/`, `/rest/v1/`, `/graphql/v1`, `/realtime/v1/api A Lua filter rejects missing or invalid keys with HTTP `401 Unauthorized`. An RBAC filter then applies finer-grained rules: -- `/pg/` - only `service_role` keys (`sb_secret_*` or legacy `SERVICE_ROLE_KEY`) are allowed (HTTP `403` otherwise). -- All other protected routes - any valid configured key is allowed. +- `/pg/` - only `service_role` keys (`sb_secret_*` or legacy `SERVICE_ROLE_KEY`) are allowed +- `/rest/v1/` (exact path, OpenAPI schema root) - only `service_role` keys are allowed +- `/rest/v1/` and other Data API paths remain accessible to all valid keys ### Opaque key translation