Skip to content
Edge Functions

使用 React Email 和 Resend 自定义认证邮件

使用 发送邮件钩子 在 Supabase Edge Functions 中通过 React EmailResend 发送自定义认证邮件。

🌐 Use the send email hook to send custom auth emails with React Email and Resend in Supabase Edge Functions.

先决条件 #

🌐 Prerequisites

要充分利用本指南,你需要:

🌐 To get the most out of this guide, you’ll need to:

确保你已经安装了最新版本的 Supabase CLI

🌐 Make sure you have the latest version of the Supabase CLI installed.

1. 创建 Supabase 函数 #

🌐 1. Create Supabase function

在本地创建一个新函数:

🌐 Create a new function locally:

1
supabase functions new send-email

2. 编辑处理函数 #

🌐 2. Edit the handler function

把以下代码粘贴到 index.ts 文件里:

🌐 Paste the following code into the index.ts file:

1
import { renderAsync } from 'npm:@react-email/components@^1'
2
import { withSupabase } from 'npm:@supabase/server@^1'
3
import React from 'npm:react@^19'
4
import { Resend } from 'npm:resend@^6'
5
import { Webhook } from 'npm:standardwebhooks@^1'
6
7
import { MagicLinkEmail } from './_templates/magic-link.tsx'
8
9
const resend = new Resend(Deno.env.get('RESEND_API_KEY') as string)
10
const hookSecret = (Deno.env.get('SEND_EMAIL_HOOK_SECRET') as string).replace('v1,whsec_', '')
11
12
export default {
13
fetch: withSupabase({ auth: 'none' }, async (req) => {
14
if (req.method !== 'POST') {
15
return Response.json({ error: 'not allowed' }, { status: 400 })
16
}
17
18
const payload = await req.text()
19
const headers = Object.fromEntries(req.headers)
20
const wh = new Webhook(hookSecret)
21
try {
22
const {
23
user,
24
email_data: { token, token_hash, redirect_to, email_action_type },
25
} = wh.verify(payload, headers) as {
26
user: {
27
email: string
28
}
29
email_data: {
30
token: string
31
token_hash: string
32
redirect_to: string
33
email_action_type: string
34
site_url: string
35
token_new: string
36
token_hash_new: string
37
}
38
}
39
40
const html = await renderAsync(
41
React.createElement(MagicLinkEmail, {
42
supabase_url: Deno.env.get('SUPABASE_URL') ?? '',
43
token,
44
token_hash,
45
redirect_to,
46
email_action_type,
47
})
48
)
49
50
const { error } = await resend.emails.send({
51
from: 'welcome <onboarding@resend.dev>',
52
to: [user.email],
53
subject: 'Supa Custom MagicLink!',
54
html,
55
})
56
if (error) {
57
throw error
58
}
59
} catch (error) {
60
console.log(error)
61
return Response.json(
62
{
63
error: {
64
http_code: error.code,
65
message: error.message,
66
},
67
},
68
{ status: 401 }
69
)
70
}
71
72
return Response.json({})
73
}),
74
}

3. 创建 React 邮件模板 #

🌐 3. Create React Email templates

按照推荐的项目结构创建一个新的_templates文件夹,并添加一个新的magic-link.tsx文件,内容如下:

🌐 Create a new _templates folder following the recommended project structure and add a new magic-link.tsx file with the following code:

1
import {
2
Body,
3
Container,
4
Head,
5
Heading,
6
Html,
7
Link,
8
Preview,
9
Text,
10
} from 'npm:@react-email/components@^1'
11
import * as React from 'npm:react@^19'
12
13
interface MagicLinkEmailProps {
14
supabase_url: string
15
email_action_type: string
16
redirect_to: string
17
token_hash: string
18
token: string
19
}
20
21
export const MagicLinkEmail = ({
22
token,
23
supabase_url,
24
email_action_type,
25
redirect_to,
26
token_hash,
27
}: MagicLinkEmailProps) => (
28
<Html>
29
<Head />
30
<Preview>Log in with this magic link</Preview>
31
<Body style={main}>
32
<Container style={container}>
33
<Heading style={h1}>Login</Heading>
34
<Link
35
href={`${supabase_url}/auth/v1/verify?token=${token_hash}&type=${email_action_type}&redirect_to=${redirect_to}`}
36
target="_blank"
37
style={{
38
...link,
39
display: 'block',
40
marginBottom: '16px',
41
}}
42
>
43
Click here to log in with this magic link
44
</Link>
45
<Text style={{ ...text, marginBottom: '14px' }}>
46
Or, copy and paste this temporary login code:
47
</Text>
48
<code style={code}>{token}</code>
49
<Text
50
style={{
51
...text,
52
color: '#ababab',
53
marginTop: '14px',
54
marginBottom: '16px',
55
}}
56
>
57
If you didn&apos;t try to login, you can safely ignore this email.
58
</Text>
59
<Text style={footer}>
60
<Link
61
href="https://demo.vercel.store/"
62
target="_blank"
63
style={{ ...link, color: '#898989' }}
64
>
65
ACME Corp
66
</Link>
67
, the famouse demo corp.
68
</Text>
69
</Container>
70
</Body>
71
</Html>
72
)
73
74
export default MagicLinkEmail
75
76
const main = {
77
backgroundColor: '#ffffff',
78
}
79
80
const container = {
81
paddingLeft: '12px',
82
paddingRight: '12px',
83
margin: '0 auto',
84
}
85
86
const h1 = {
87
color: '#333',
88
fontFamily:
89
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
90
fontSize: '24px',
91
fontWeight: 'bold',
92
margin: '40px 0',
93
padding: '0',
94
}
95
96
const link = {
97
color: '#2754C5',
98
fontFamily:
99
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
100
fontSize: '14px',
101
textDecoration: 'underline',
102
}
103
104
const text = {
105
color: '#333',
106
fontFamily:
107
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
108
fontSize: '14px',
109
margin: '24px 0',
110
}
111
112
const footer = {
113
color: '#898989',
114
fontFamily:
115
"-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif",
116
fontSize: '12px',
117
lineHeight: '22px',
118
marginTop: '12px',
119
marginBottom: '24px',
120
}
121
122
const code = {
123
display: 'inline-block',
124
padding: '16px 4.5%',
125
width: '90.5%',
126
backgroundColor: '#f4f4f4',
127
borderRadius: '5px',
128
border: '1px solid #eee',
129
color: '#333',
130
}

4. 部署函数 #

🌐 4. Deploy the Function

将函数部署到 Supabase:

🌐 Deploy function to Supabase:

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

记下这个函数的 URL,下一步你会用到它!

🌐 Note down the function URL, you will need it in the next step!

5. 配置发送邮件钩子 #

🌐 5. Configure the Send Email Hook

  • 去 Supabase 控制面板的 Auth Hooks 部分,创建一个新的“发送邮件钩子”。
  • 选择 HTTPS 作为钩子类型。
  • 把函数的 URL 粘贴到“URL”字段里。
  • 点击“生成密钥”来生成你的 webhook 密钥并记下来。
  • 点击“创建”以保存钩子配置。

把这些秘密存到你的 .env 文件里。

🌐 Store these secrets in your .env file.

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

.env 文件中设置秘密:

🌐 Set the secrets from the .env file:

1
supabase secrets set --env-file supabase/functions/.env

现在,每当需要向用户发送认证邮件时,你的 Supabase Edge 函数都会被触发!

🌐 Now your Supabase Edge Function will be triggered anytime an Auth Email needs to be sent to the user!

更多资源 #

🌐 More resources