邮件服务

Better Auth Infrastructure 提供托管式事务邮件服务,并为常见身份验证流程提供预构建模板。无需管理邮件基础设施,即可发送验证邮件、密码重置邮件、邀请邮件等。

概述

邮件服务提供:

  • 预构建的专业设计邮件模板
  • 多供应商支持(AWS SES、SendGrid、Resend)
  • 类型安全的模板变量
  • 无需管理的基础设施
  • 投递优化

安装

邮件服务包含在 @better-auth/infra 包中:

import { sendEmail, createEmailSender } from "@better-auth/infra";

快速开始

发送单个邮件

import { sendEmail } from "@better-auth/infra";

await sendEmail({
  template: "verify-email",
  to: "[email protected]",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc123",
    userEmail: "[email protected]",
    userName: "John",
    appName: "Your App",
  },
});

创建可复用的发送器

import { createEmailSender } from "@better-auth/infra";

const emailSender = createEmailSender({
  apiKey: process.env.BETTER_AUTH_API_KEY,
  apiUrl: process.env.BETTER_AUTH_API_URL,
});

// 发送多封邮件
await emailSender.send({
  template: "reset-password",
  to: "[email protected]",
  variables: {
    resetLink: "https://yourapp.com/reset?token=xyz",
    userEmail: "[email protected]",
  },
});

可用模板

verify-email

向新用户发送邮件验证链接。

await sendEmail({
  template: "verify-email",
  to: "[email protected]",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc",
    userEmail: "[email protected]",
    verificationCode: "123456",        // 可选:基于验证码的验证
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    expirationMinutes: "60",           // 可选
  },
});

reset-password

发送密码重置链接。

await sendEmail({
  template: "reset-password",
  to: "[email protected]",
  variables: {
    resetLink: "https://yourapp.com/reset?token=xyz",
    userEmail: "[email protected]",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    expirationMinutes: "60",           // 可选
  },
});

change-email

确认邮箱地址变更请求。

await sendEmail({
  template: "change-email",
  to: "[email protected]",
  variables: {
    confirmationLink: "https://yourapp.com/confirm-email?token=abc",
    newEmail: "[email protected]",
    currentEmail: "[email protected]",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    expirationMinutes: "60",           // 可选
  },
});

sign-in-otp

发送一次性密码(OTP)用于无密码登录。

await sendEmail({
  template: "sign-in-otp",
  to: "[email protected]",
  variables: {
    otpCode: "123456",
    userEmail: "[email protected]",
    appName: "Your App",               // 可选
    expirationMinutes: "10",           // 可选
  },
});

verify-email-otp

发送邮件验证用的 OTP 码。

await sendEmail({
  template: "verify-email-otp",
  to: "[email protected]",
  variables: {
    otpCode: "123456",
    userEmail: "[email protected]",
    appName: "Your App",               // 可选
    expirationMinutes: "10",           // 可选
  },
});

reset-password-otp

发送密码重置用的 OTP 码。

await sendEmail({
  template: "reset-password-otp",
  to: "[email protected]",
  variables: {
    otpCode: "123456",
    userEmail: "[email protected]",
    appName: "Your App",               // 可选
    expirationMinutes: "10",           // 可选
  },
});

发送无密码认证用的魔法链接。

await sendEmail({
  template: "magic-link",
  to: "[email protected]",
  variables: {
    magicLink: "https://yourapp.com/auth/magic?token=abc",
    userEmail: "[email protected]",
    appName: "Your App",               // 可选
    expirationMinutes: "15",           // 可选
  },
});

two-factor

发送两步验证代码。

await sendEmail({
  template: "two-factor",
  to: "[email protected]",
  variables: {
    otpCode: "123456",
    userEmail: "[email protected]",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    expirationMinutes: "5",            // 可选
  },
});

invitation

发送组织邀请。

await sendEmail({
  template: "invitation",
  to: "[email protected]",
  variables: {
    inviteLink: "https://yourapp.com/invite?token=abc",
    inviterName: "John Smith",
    inviterEmail: "[email protected]",
    organizationName: "Acme Corp",
    role: "Member",
    appName: "Your App",               // 可选
    expirationDays: "7",               // 可选
  },
});

application-invite

发送应用级邀请(邀请用户加入平台)。

await sendEmail({
  template: "application-invite",
  to: "[email protected]",
  variables: {
    inviteLink: "https://yourapp.com/join?token=abc",
    inviterName: "John Smith",
    inviterEmail: "[email protected]",
    inviteeEmail: "[email protected]",
    appName: "Your App",               // 可选
    expirationDays: "7",               // 可选
  },
});

delete-account

发送账户删除确认。

await sendEmail({
  template: "delete-account",
  to: "[email protected]",
  variables: {
    deletionLink: "https://yourapp.com/confirm-delete?token=abc",
    userEmail: "[email protected]",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    expirationMinutes: "60",           // 可选
  },
});

stale-account-user

通知用户其闲置账户被访问。

await sendEmail({
  template: "stale-account-user",
  to: "[email protected]",
  variables: {
    userEmail: "[email protected]",
    daysSinceLastActive: "90",
    loginTime: "2026年2月20日 15:45 UTC",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    loginLocation: "纽约,美国",         // 可选
    loginDevice: "Windows 下的 Chrome",   // 可选
    loginIp: "192.168.1.1",            // 可选
  },
});

stale-account-admin

通知管理员闲置账户重新激活。

await sendEmail({
  template: "stale-account-admin",
  to: "[email protected]",
  variables: {
    userEmail: "[email protected]",
    userId: "user_123",
    adminEmail: "[email protected]",
    daysSinceLastActive: "90",
    loginTime: "2026年2月20日 15:45 UTC",
    userName: "John",                   // 可选
    appName: "Your App",               // 可选
    loginLocation: "纽约,美国",         // 可选
    loginDevice: "Windows 下的 Chrome",   // 可选
    loginIp: "192.168.1.1",            // 可选
  },
});

配置

EmailConfig

interface EmailConfig {
  apiKey?: string;   // 你的 Better Auth Infrastructure API 密钥
  apiUrl?: string;   // 自定义 API URL(可选)
}

环境变量

邮件服务会自动读取以下环境变量:

BETTER_AUTH_API_KEY=your_api_key_here
BETTER_AUTH_API_URL=https://api.betterauth.com  # 可选

返回格式

SendEmailResult

interface SendEmailResult {
  success: boolean;
  messageId?: string;  // 邮件提供商的消息 ID
  error?: string;      // 失败时的错误信息
}

使用示例

const result = await sendEmail({
  template: "verify-email",
  to: "[email protected]",
  variables: {
    verificationUrl: "https://yourapp.com/verify?token=abc",
    userEmail: "[email protected]",
  },
});

if (result.success) {
  console.log("邮件已发送:", result.messageId);
} else {
  console.error("邮件发送失败:", result.error);
}

套餐要求

功能StarterProBusinessEnterprise
事务邮件-YesYesYes

与 Better Auth 集成

与 Better Auth 集成

邮件服务与 Better Auth 的身份验证流程无缝集成。以下是一个完整示例:

auth.ts
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendEmail } from "@better-auth/infra";

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    async sendResetPassword({ user, url }) {
      await sendEmail({
        template: "reset-password",
        to: user.email,
        variables: {
          resetLink: url,
          userEmail: user.email,
          userName: user.name,
          appName: "Your App",
        },
      });
    },
  },
  emailVerification: {
    sendOnSignUp: true,
    async sendVerificationEmail({ user, url }) {
      await sendEmail({
        template: "verify-email",
        to: user.email,
        variables: {
          verificationUrl: url,
          userEmail: user.email,
          userName: user.name,
          appName: "Your App",
        },
      });
    },
  },
  plugins: [
    organization({
      async sendInvitationEmail(data) {
        const inviteLink = `https://yourapp.com/accept-invitation/${data.id}`;
        await sendEmail({
          template: "invitation",
          to: data.email,
          variables: {
            inviteLink,
            inviterName: data.inviter.user.name,
            inviterEmail: data.inviter.user.email,
            organizationName: data.organization.name,
            role: data.role,
            appName: "Your App",
          },
        });
      },
    }),
  ],
});

在生产环境中避免等待邮件发送完成以防止时间攻击。在无服务器平台上,请使用 waitUntil 或类似方式确保邮件发送完成,同时不阻塞响应。