保护边缘功能
Authentication patterns for Edge Functions
通过声明它接受哪些凭据来保护 Edge 函数。GitHub 上 @supabase/server 包 的 withSupabase 封装器会根据你设置的 auth 模式检查每个调用者。你的处理器会在 ctx 上收到一个预配置的 Supabase 客户端。
🌐 Secure an Edge Function by declaring which credentials it accepts. The withSupabase wrapper from the @supabase/server package on GitHub checks each caller against the auth mode you set. Your handler receives a preconfigured Supabase client on ctx.
- 选择一种认证模式 对比了四种模式。如果你不确定你的函数需要哪一种,可以从这里开始。
- 保护你的函数 对每种模式都有示例,还说明了如何组合模式并自定义错误响应。
- 环境变量 列出了
@supabase/server从环境中读取的内容。
选择一种认证模式 #
🌐 Choose an auth mode
每种模式都会命名调用者必须提供的凭证。withSupabase 接受四种:
🌐 Each mode names the credential a caller must present. withSupabase accepts four:
| 模式 | 接受的内容 |
|---|---|
'user' | 在 Authorization 上的有效用户 JWT |
'secret' | 在 apikey 上的 default 秘钥 |
'publishable' | 在 apikey 上的 default 可发布密钥 |
'none' | 任何调用者,不做检查(用于签名的 webhook) |
有关授权头和 verify_jwt 平台检查的工作原理,请参见 Authorization headers。
🌐 For how authorization headers and the verify_jwt platform check work, see Authorization headers.
保护你的功能 #
🌐 Secure your function
下面的每个部分都会讲一个模式,然后讲如何组合模式并塑造你自己的错误响应。每个示例都会把处理器包在 withSupabase 中,并从 ctx 获取它的 Supabase 客户端。
🌐 Each of the following sections covers one mode, then how to combine modes and shape your own error response. Every example wraps the handler in withSupabase and reads its Supabase client from ctx.
已认证用户调用 #
🌐 Authenticated user calls
当已登录的用户调用一个函数时,请求会在 Authorization 头中携带用户的会话 JWT。你的应用通常通过 supabase.functions.invoke 发起这个调用。使用 auth: 'user' 来获取一个根据调用者的行级安全(RLS)策略范围的 ctx.supabase。
🌐 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. Use auth: 'user' to get a ctx.supabase scoped to the caller's Row Level Security (RLS) policies.
默认是 verify_jwt = true,所以平台会在你的处理程序运行之前验证 JWT。对面向用户的功能保持开启。在开发期间为了消除 401 而关闭它,会移除平台的检查,只剩下 withSupabase 来验证调用者。
🌐 The default is verify_jwt = true, so the platform validates the JWT before your handler runs. Leave it on for user-facing functions. Turning it off to clear a 401 during development removes the platform's check, leaving withSupabase as the only thing that verifies the caller.
import { withSupabase } from 'npm:@supabase/server@1'export default { fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => { const { supabase, supabaseAdmin, userClaims, jwtClaims, authMode } = ctx // supabase — RLS-scoped to the authenticated user // supabaseAdmin — bypasses RLS (service role) // userClaims — user identity from JWT (id, email, role) // jwtClaims — full JWT claims // authMode — which auth mode matched // your business logic goes here return Response.json({ email: ctx.userClaims?.email }) }),}在处理已登录的调用者时,使用 ctx.supabase。它会将他们的 RLS 策略应用到每个查询。仅在工作必须跨越这些策略时才使用 ctx.supabaseAdmin。
🌐 Reach for ctx.supabase when the handler serves the signed-in caller. It applies their RLS policies to every query. Use ctx.supabaseAdmin only for work that has to cross those policies.
一个通过 ctx.supabaseAdmin 查询共享表而不按调用者 ID 过滤的处理程序会返回每个用户的行。ctx.supabaseAdmin 会绕过行级安全性,所以要么从 ctx.userClaims 按调用者 ID 过滤,要么直接用 ctx.supabase。
🌐 A handler that queries a shared table through ctx.supabaseAdmin without filtering by the caller's ID returns every user's rows. ctx.supabaseAdmin bypasses Row Level Security, so filter by the caller's ID from ctx.userClaims, or use ctx.supabase instead.
服务间调用 #
🌐 Service-to-service calls
Cron 作业、workers、pg_net 和其他 Edge Functions 都是通过 apikey 头部的密钥来调用,而不是使用用户 JWT。禁用 verify_jwt 并使用 auth: 'secret'。这个封装会将密钥与你项目的 API 密钥中名为 default 的密钥进行验证,并为你的处理程序 ctx.supabaseAdmin 提供特权操作。
🌐 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 the secret key named default in your project's API keys, and gives your handler ctx.supabaseAdmin for privileged work.
import { withSupabase } from 'npm:@supabase/server@1'export default { fetch: withSupabase({ auth: 'secret' }, async (_req, ctx) => { // your business logic. ctx.supabaseAdmin bypasses RLS return Response.json({ ok: true }) }),}auth: 'secret' 只接受名为 default 的密钥。要接受不同的密钥,请使用 auth: 'secret:<name>'。例如,auth: 'secret:automations' 只接受名为 automations 的秘密密钥。要在项目中接受任何秘密密钥,请使用 auth: 'secret:*'。每个项目都有一个名为 default 的秘密密钥,你也可以添加更多。要给新密钥命名,请在 Supabase 控制面板中打开 设置 > API 密钥。相同的语法也适用于可发布密钥:auth: 'publishable:<name>' 和 auth: 'publishable:*'。

公共函数 #
🌐 Public functions
对于一个真正的公共功能,比如健康检查,使用 auth: 'none' 和 verify_jwt = false,这样匿名调用者也能访问处理程序。
🌐 For a genuinely public function, such as a health check, use auth: 'none' with verify_jwt = false so anonymous callers can reach the handler.
[functions.health]verify_jwt = falseimport { withSupabase } from 'npm:@supabase/server@1'export default { fetch: withSupabase({ auth: 'none' }, async () => { // your business logic return Response.json({ ok: true }) }),}auth: 'none' 接受所有调用者。有关自行验证调用者的函数,请参见本页的 外部 Webhook 部分。
外部网络钩子 #
🌐 External webhooks
像 Stripe 或 GitHub 这样的外部提供商不会发送 Supabase 凭据。他们会用自己的共享密钥对请求体进行签名。使用 auth: 'none' 来跳过封装器的凭据检查,然后在处理器内验证提供商的签名。保留 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.
import { withSupabase } from 'npm:@supabase/server@1'import Stripe from 'npm:stripe'const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!)// Deno has no synchronous Node crypto, so signature verification must go through// Stripe's SubtleCryptoProvider. The synchronous `constructEvent()` throws// "SubtleCryptoProvider cannot be used in a synchronous context" on this runtime.const cryptoProvider = Stripe.createSubtleCryptoProvider()export default { fetch: withSupabase({ auth: 'none' }, async (req, ctx) => { const signature = req.headers.get('stripe-signature') ?? '' const body = await req.text() try { await stripe.webhooks.constructEventAsync( body, signature, Deno.env.get('STRIPE_WEBHOOK_SECRET')!, undefined, cryptoProvider ) } catch (err) { // Log the reason so a configuration error isn't mistaken for a forged payload. console.error('Stripe signature verification failed:', err) return new Response('bad signature', { status: 400 }) } // your business logic. ctx.supabaseAdmin available for database work return Response.json({ received: true }) }),}auth: 'none' 会禁用所有的凭证检查,所以你的处理器需要完全负责验证调用者。当函数读取或写入敏感数据时,需要自己在处理器里验证调用者。
组合模式 #
🌐 Combining modes
既响应用户又响应内部调用的函数会在 auth 上接收一个数组。封装器会按顺序尝试每种模式,并使用第一个匹配的模式。ctx.authMode 会告诉你哪个模式匹配了。
🌐 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.
import { withSupabase } from 'npm:@supabase/server@1'export default { fetch: withSupabase({ auth: ['user', 'secret'] }, async (req, ctx) => { if (ctx.authMode === 'user') { // your business logic for user calls. ctx.supabase is scoped to them return Response.json({ ok: true }) } // your business logic for service calls. ctx.supabaseAdmin bypasses RLS return Response.json({ ok: true }) }),}自定义错误响应 #
🌐 Custom error responses
要自己处理 401 响应,使用 createSupabaseContext 替代 withSupabase。它会返回一个 { data, error } 元组,而不是帮你拒绝请求。
🌐 To shape the 401 response yourself, use createSupabaseContext instead of withSupabase. It returns a { data, error } tuple instead of rejecting the request for you.
import { createSupabaseContext } from 'npm:@supabase/server@1'export default { fetch: async (req: Request) => { const { data: ctx, error } = await createSupabaseContext(req, { auth: 'user' }) if (error) { return Response.json({ message: error.message, code: error.code }, { status: error.status }) } return Response.json({ message: `hello ${ctx.userClaims?.email}` }) },}环境变量 #
🌐 Environment variables
@supabase/server 从一组标准的环境变量中读取其配置,这些变量由 Supabase 平台和 Supabase CLI 为你提供。
| 变量 | 它是什么 |
|---|---|
SUPABASE_URL | 你的项目网址 |
SUPABASE_PUBLISHABLE_KEYS | 以 JSON 对象形式命名的可发布密钥 |
SUPABASE_SECRET_KEYS | 以 JSON 对象形式命名的秘密密钥 |
SUPABASE_JWKS | 用于验证用户 JWT 的 JSON Web 密钥集 |
使用 CLI 进行本地开发时采用单键设置。@supabase/server 也可以接受 SUPABASE_PUBLISHABLE_KEY 和 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.
@supabase/server 在其他运行时上的配置方式是一样的。把它安装到你的 Node.js、Bun、Cloudflare Workers 或自托管的 Deno 应用中,然后设置相同的变量。完整指南请参阅 @supabase/server 仓库中的环境变量指南。