发送邮件钩子
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 | 与邮件发送过程相关的元数据 |
1{2 "user": {3 "id": "8484b834-f29e-4af2-bf42-80644d154f76",4 "aud": "authenticated",5 "role": "authenticated",6 "email": "valid.email@supabase.io",7 "phone": "",8 "app_metadata": {9 "provider": "email",10 "providers": ["email"]11 },12 "user_metadata": {13 "email": "valid.email@supabase.io",14 "email_verified": false,15 "phone_verified": false,16 "sub": "8484b834-f29e-4af2-bf42-80644d154f76"17 },18 "identities": [19 {20 "identity_id": "bc26d70b-517d-4826-bce4-413a5ff257e7",21 "id": "8484b834-f29e-4af2-bf42-80644d154f76",22 "user_id": "8484b834-f29e-4af2-bf42-80644d154f76",23 "identity_data": {24 "email": "valid.email@supabase.io",25 "email_verified": false,26 "phone_verified": false,27 "sub": "8484b834-f29e-4af2-bf42-80644d154f76"28 },29 "provider": "email",30 "last_sign_in_at": "2024-05-14T12:56:33.824231484Z",31 "created_at": "2024-05-14T12:56:33.824261Z",32 "updated_at": "2024-05-14T12:56:33.824261Z",33 "email": "valid.email@supabase.io"34 }35 ],36 "created_at": "2024-05-14T12:56:33.821567Z",37 "updated_at": "2024-05-14T12:56:33.825595Z",38 "is_anonymous": false39 },40 "email_data": {41 "token": "305805",42 "token_hash": "7d5b7b1964cf5d388340a7f04f1dbb5eeb6c7b52ef8270e1737a58d0",43 "redirect_to": "http://localhost:3000/",44 "email_action_type": "signup",45 "site_url": "http://localhost:9999",46 "token_new": "",47 "token_hash_new": "",48 "old_email": "",49 "old_phone": "",50 "provider": "",51 "factor_type": ""52 }53}输出
- 不需要输出。空响应且状态码为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:
1RESEND_API_KEY="your_resend_api_key"2SEND_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:
1supabase secrets set --env-file .env创建一个新的边缘函数:
🌐 Create a new edge function:
1supabase functions new send-email把以下代码加到你的边缘函数里:
🌐 Add the following code to your edge function:
1import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0";2import { Resend } from "npm:resend";34const resend = new Resend(Deno.env.get("RESEND_API_KEY") as string);5const hookSecret = (Deno.env.get("SEND_EMAIL_HOOK_SECRET") as string).replace("v1,whsec_", "");67Deno.serve(async (req) => {8 if (req.method !== "POST") {9 return new Response("not allowed", { status: 400 });10 }1112 const payload = await req.text();13 const headers = Object.fromEntries(req.headers);14 const wh = new Webhook(hookSecret);15 try {16 const { user, email_data } = wh.verify(payload, headers) as {17 user: {18 email: string;19 };20 email_data: {21 token: string;22 token_hash: string;23 redirect_to: string;24 email_action_type: string;25 site_url: string;26 token_new: string;27 token_hash_new: string;28 };29 };3031 const { error } = await resend.emails.send({32 from: "welcome <onboarding@example.com>",33 to: [user.email],34 subject: "Welcome to my site!",35 text: `Confirm you signup with this code: ${email_data.token}`,36 });37 if (error) {38 throw error;39 }40 } catch (error) {41 return new Response(42 JSON.stringify({43 error: {44 http_code: error.code,45 message: error.message,46 },47 }),48 {49 status: 401,50 headers: { "Content-Type": "application/json" },51 },52 );53 }5455 const responseHeaders = new Headers();56 responseHeaders.set("Content-Type", "application/json");57 return new Response(JSON.stringify({}), {58 status: 200,59 headers: responseHeaders,60 });61});部署你的边缘函数并将其配置为钩子:
🌐 Deploy your edge function and configure it as a hook:
1supabase functions deploy send-email --no-verify-jwt