发送邮件钩子
Use your own email service to send authentication emails.
发送邮件 Hook 替代了 Supabase 内置的邮件发送功能。你可以使用这个 Hook 来:
🌐 The Send Email Hook replaces Supabase's built-in email sending. You can use this hook to:
- 使用你自己的邮箱发送邮件
- 添加国际化或自定义逻辑
- 如果你的主要服务提供商失败,就换另一个吧
输入
| 字段 | 类型 | 描述 |
|---|---|---|
user | User | 执行动作的用户账户 |
email | object | 与邮件发送过程相关的元数据 |
{ "user": { "id": "8484b834-f29e-4af2-bf42-80644d154f76", "aud": "authenticated", "role": "authenticated", "email": "valid.email@supabase.io", "phone": "", "app_metadata": { "provider": "email", "providers": ["email"] }, "user_metadata": { "email": "valid.email@supabase.io", "email_verified": false, "phone_verified": false, "sub": "8484b834-f29e-4af2-bf42-80644d154f76" }, "identities": [ { "identity_id": "bc26d70b-517d-4826-bce4-413a5ff257e7", "id": "8484b834-f29e-4af2-bf42-80644d154f76", "user_id": "8484b834-f29e-4af2-bf42-80644d154f76", "identity_data": { "email": "valid.email@supabase.io", "email_verified": false, "phone_verified": false, "sub": "8484b834-f29e-4af2-bf42-80644d154f76" }, "provider": "email", "last_sign_in_at": "2024-05-14T12:56:33.824231484Z", "created_at": "2024-05-14T12:56:33.824261Z", "updated_at": "2024-05-14T12:56:33.824261Z", "email": "valid.email@supabase.io" } ], "created_at": "2024-05-14T12:56:33.821567Z", "updated_at": "2024-05-14T12:56:33.825595Z", "is_anonymous": false }, "email_data": { "token": "305805", "token_hash": "7d5b7b1964cf5d388340a7f04f1dbb5eeb6c7b52ef8270e1737a58d0", "redirect_to": "http://localhost:3000/", "email_action_type": "signup", "site_url": "http://localhost:9999", "token_new": "", "token_hash_new": "", "old_email": "", "old_phone": "", "provider": "", "factor_type": "" }}输出
- 不需要输出。空响应且状态码为200被视为成功响应。
发送邮件的行为 #
🌐 Email sending behavior
发邮件取决于两个设置:邮件提供商和认证钩子状态。
🌐 Email sending depends on two settings: Email Provider and Auth Hook status.
| 邮箱服务提供商 | 认证钩子 | 结果 |
|---|---|---|
| 启用 | 启用 | 认证钩子处理邮件发送(不使用 SMTP) |
| 启用 | 禁用 | SMTP 处理邮件发送(如果配置了则使用自定义,否则使用默认) |
| 禁用 | 启用 | 邮箱注册已禁用 |
| 禁用 | 禁用 | 邮箱注册已禁用 |
邮箱更改行为和令牌哈希映射 #
🌐 Email change behavior and token hash mapping
当 email_action_type 是 email_change 时,钩子负载可以包含一个或两个 OTP 及其哈希。这取决于你的 安全邮箱更改 设置。
🌐 When email_action_type is email_change, the hook payload can include one or two OTPs and their hashes. This depends on your Secure Email Change setting.
- 启用安全邮箱更改:会生成两个一次性密码,一个用于当前邮箱(
user.email),另一个用于新邮箱(user.new_email)。你需要发送两封邮件。 - 安全邮箱更改已禁用:新邮箱只会生成一个一次性密码。你只需发送一封邮件。
反直觉的字段命名
由于向后兼容,令牌哈希字段名称是颠倒的。请仔细注意每个令牌/哈希对对应哪个电子邮件地址:
🌐 The token hash field names are reversed due to backward compatibility. Pay careful attention to which token/hash pair goes with which email address:
token_hash_new→ 使用 当前 邮箱地址 (user.email) 和tokentoken_hash→ 使用 新的 邮箱地址 (user.new_email) 和token_new
不要以为 _new 后缀指的是新的电子邮件地址。
🌐 Do not assume the _new suffix refers to the new email address.
发送什么 #
🌐 What to send
当启用安全电子邮件更改时(存在令牌/哈希对):
🌐 When Secure Email Change is enabled (both token/hash pairs present):
- 发送到当前邮箱地址(
user.email):用token和token_hash_new - 发送到新邮箱地址(
user.new_email):用token_new和token_hash
当安全邮件更改禁用时(仅存在一对令牌/哈希):
🌐 When Secure Email Change is disabled (only one token/hash pair present):
- 给新的邮箱地址发送一封邮件。根据 payload 中有哪些字段,使用
token配合token_hash,或者token_new配合token_hash。
你可以通过“发送邮件”钩子将 Resend 配置为自定义邮件提供商。这样你就可以利用 Resend 的开发者友好型 API 来发送邮件,并使用 React Email 来管理你的邮件模板。想要更高级的 React Email 教程,可以参考 这个指南。
如果你想通过 Supabase Resend 集成发送电子邮件,该集成使用 Resend 的 SMTP 服务器,可以看看这个集成。
🌐 If you want to send emails through the Supabase Resend integration, which uses Resend's SMTP server, check out this integration instead.
创建一个 .env 文件,并设置以下环境变量:
🌐 Create a .env file with the following environment variables:
RESEND_API_KEY="your_resend_api_key"SEND_EMAIL_HOOK_SECRET="v1,whsec_<base64_secret>"你可以在 Supabase 仪表板的 Auth Hooks 部分生成密钥。
🌐 You can generate the secret in the Auth Hooks section of the Supabase dashboard.
在你的 Supabase 项目中设置密钥:
🌐 Set the secrets in your Supabase project:
supabase secrets set --env-file .env创建一个新的边缘函数:
🌐 Create a new edge function:
supabase functions new send-email把以下代码加到你的边缘函数里:
🌐 Add the following code to your edge function:
import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0";import { Resend } from "npm:resend";const resend = new Resend(Deno.env.get("RESEND_API_KEY") as string);const hookSecret = (Deno.env.get("SEND_EMAIL_HOOK_SECRET") as string).replace("v1,whsec_", "");Deno.serve(async (req) => { if (req.method !== "POST") { return new Response("not allowed", { status: 400 }); } const payload = await req.text(); const headers = Object.fromEntries(req.headers); const wh = new Webhook(hookSecret); try { const { user, email_data } = wh.verify(payload, headers) as { user: { email: string; }; email_data: { token: string; token_hash: string; redirect_to: string; email_action_type: string; site_url: string; token_new: string; token_hash_new: string; }; }; const { error } = await resend.emails.send({ from: "welcome <onboarding@example.com>", to: [user.email], subject: "Welcome to my site!", text: `Confirm you signup with this code: ${email_data.token}`, }); if (error) { throw error; } } catch (error) { return new Response( JSON.stringify({ error: { http_code: error.code, message: error.message, }, }), { status: 401, headers: { "Content-Type": "application/json" }, }, ); } const responseHeaders = new Headers(); responseHeaders.set("Content-Type", "application/json"); return new Response(JSON.stringify({}), { status: 200, headers: responseHeaders, });});部署你的边缘函数并将其配置为钩子:
🌐 Deploy your edge function and configure it as a hook:
supabase functions deploy send-email --no-verify-jwt