Skip to content
Auth

发送邮件钩子

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:

  • 使用你自己的邮箱发送邮件
  • 添加国际化或自定义逻辑
  • 如果你的主要服务提供商失败,就换另一个吧

输入

字段类型描述
userUser执行动作的用户账户
emailobject与邮件发送过程相关的元数据
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": false
39
},
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_typeemail_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)。你需要发送两封邮件。
  • 安全邮箱更改已禁用:新邮箱只会生成一个一次性密码。你只需发送一封邮件。

发送什么 #

🌐 What to send

当启用安全电子邮件更改时(存在令牌/哈希对):

🌐 When Secure Email Change is enabled (both token/hash pairs present):

  • 发送到当前邮箱地址(user.email):用tokentoken_hash_new
  • 发送到邮箱地址(user.new_email):用token_newtoken_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:

1
RESEND_API_KEY="your_resend_api_key"
2
SEND_EMAIL_HOOK_SECRET="v1,whsec_<base64_secret>"

在你的 Supabase 项目中设置密钥:

🌐 Set the secrets in your Supabase project:

1
supabase secrets set --env-file .env

创建一个新的边缘函数:

🌐 Create a new edge function:

1
supabase functions new send-email

把以下代码加到你的边缘函数里:

🌐 Add the following code to your edge function:

1
import { Webhook } from "https://esm.sh/standardwebhooks@1.0.0";
2
import { Resend } from "npm:resend";
3
4
const resend = new Resend(Deno.env.get("RESEND_API_KEY") as string);
5
const hookSecret = (Deno.env.get("SEND_EMAIL_HOOK_SECRET") as string).replace("v1,whsec_", "");
6
7
Deno.serve(async (req) => {
8
if (req.method !== "POST") {
9
return new Response("not allowed", { status: 400 });
10
}
11
12
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
};
30
31
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
}
54
55
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:

1
supabase functions deploy send-email --no-verify-jwt