电子邮件 OTP

用于 Better Auth 的电子邮件 OTP 插件。

电子邮件 OTP 插件允许用户通过发送到其电子邮件地址的一次性密码(OTP)进行登录、验证电子邮件或重置密码。

安装

将插件添加到您的身份验证配置中

emailOTP 插件添加到您的身份验证配置,并实现 sendVerificationOTP() 方法。

auth.ts
import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"

export const auth = betterAuth({
    // ... 其他配置选项
    plugins: [
        emailOTP({ 
            async sendVerificationOTP({ email, otp, type }) { 
                if (type === "sign-in") { 
                    // 发送登录的 OTP
                } else if (type === "email-verification") { 
                    // 发送电子邮件验证的 OTP
                } else { 
                    // 发送密码重置的 OTP
                } 
            }, 
        }) 
    ]
})

添加客户端插件

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { emailOTPClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [
        emailOTPClient() 
    ]
})

使用方法

发送 OTP

使用 sendVerificationOtp() 方法向用户的电子邮件地址发送 OTP。

POST/email-otp/send-verification-otp
const { data, error } = await authClient.emailOtp.sendVerificationOtp({    email: "user@example.com", // required    type: "sign-in", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

type"email-verification" | "sign-in" | "forget-password"required

OTP 的类型。sign-inemail-verificationforget-password

校验 OTP(可选)

使用 checkVerificationOtp() 方法验证 OTP 是否有效。

POST/email-otp/check-verification-otp
const { data, error } = await authClient.emailOtp.checkVerificationOtp({    email: "user@example.com", // required    type: "sign-in", // required    otp: "123456", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

type"email-verification" | "sign-in" | "forget-password"required

OTP 的类型。sign-inemail-verificationforget-password

otpstringrequired

发送到电子邮件的 OTP。

使用 OTP 登录

使用 OTP 登录时,先通过 sendVerificationOtp() 方法向用户电子邮件发送类型为 "sign-in" 的 OTP。

POST/email-otp/send-verification-otp
const { data, error } = await authClient.emailOtp.sendVerificationOtp({    email: "user@example.com", // required    type: "sign-in", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

type"sign-in"required

OTP 的类型。

用户提供 OTP 后,可使用 signIn.emailOtp() 方法登录用户。

POST/sign-in/email-otp
const { data, error } = await authClient.signIn.emailOtp({    email: "user@example.com", // required    otp: "123456", // required    name: "John Doe",    image: "https://example.com/image.png",});
Parameters
emailstringrequired

登录所用的电子邮件地址。

otpstringrequired

发送到电子邮件的 OTP。

namestring

用户显示名称。仅在用户首次注册时使用。

imagestring

用户头像 URL。仅在用户首次注册时使用。

如果用户未注册,他们将自动注册。为新用户也会接受已配置的附加字段。若要阻止自动注册,请在选项中将 disableSignUp 设为 true

当签名 OTP 确认了一个已有账户且该账户的电子邮件从未验证过时,该账户上任何现有密码都会被移除,其会话也会被撤销。用户会通过 OTP 登录,并可通过密码重置设置新密码。这样可确保由 OTP 证明的邮箱所有权作为该账户的事实来源。

使用 OTP 验证电子邮件

使用 OTP 验证用户邮箱时,先通过 sendVerificationOtp() 方法向用户邮箱发送类型为 "email-verification" 的 OTP。

POST/email-otp/send-verification-otp
const { data, error } = await authClient.emailOtp.sendVerificationOtp({    email: "user@example.com", // required    type: "email-verification", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

type"email-verification"required

OTP 的类型。

用户输入 OTP 后,使用 verifyEmail() 方法完成邮箱验证。

POST/email-otp/verify-email
const { data, error } = await authClient.emailOtp.verifyEmail({    email: "user@example.com", // required    otp: "123456", // required});
Parameters
emailstringrequired

要验证的电子邮件地址。

otpstringrequired

用于验证的 OTP。

使用 OTP 重置密码

使用 OTP 重置密码时,使用 emailOtp.requestPasswordReset() 方法向用户邮箱发送类型为 "forget-password" 的 OTP。

POST/email-otp/request-password-reset
const { data, error } = await authClient.emailOtp.requestPasswordReset({    email: "user@example.com", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

/forget-password/email-otp 端点已弃用。请改用 /email-otp/request-password-reset

用户提供 OTP 后,可以选择使用 checkVerificationOtp() 方法验证 OTP 是否有效。

POST/email-otp/check-verification-otp
const { data, error } = await authClient.emailOtp.checkVerificationOtp({    email: "user@example.com", // required    type: "forget-password", // required    otp: "123456", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

type"forget-password"required

OTP 的类型。

otpstringrequired

发送到电子邮件的 OTP。

然后,使用 resetPassword() 方法重置用户密码。

POST/email-otp/reset-password
const { data, error } = await authClient.emailOtp.resetPassword({    email: "user@example.com", // required    otp: "123456", // required    password: "new-secure-password", // required});
Parameters
emailstringrequired

用于重置密码的电子邮件地址。

otpstringrequired

发送到电子邮件的 OTP。

passwordstringrequired

新密码。

使用 OTP 更改电子邮件

要允许用户使用 OTP 更改电子邮件,需先启用 changeEmail 功能,该功能默认关闭。将 changeEmail.enabled 设置为 true

auth.ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    plugins: [
        emailOTP({
            changeEmail: {
                enabled: true, 
            }
        })
    ]
})

默认情况下,当用户请求更改电子邮件时,OTP 会发送到新的电子邮件地址。只有用户验证了新邮箱后,邮箱才会被更新。

使用方法

要更改用户电子邮件地址,使用 emailOtp.requestEmailChange() 方法向用户新的电子邮件地址发送类型为 "change-email" 的 OTP。

POST/email-otp/request-email-change
const { data, error } = await authClient.emailOtp.requestEmailChange({    newEmail: "user@example.com", // required    otp: "123456",});
Parameters
newEmailstringrequired

要发送 OTP 的新电子邮件地址。

otpstring

发送到当前邮箱的 OTP。 当 changeEmail.verifyCurrentEmail 选项设置为 true 时必需。

用户提供 OTP 后,使用 changeEmail() 方法更改用户邮箱地址。

POST/email-otp/change-email
const { data, error } = await authClient.emailOtp.changeEmail({    newEmail: "user@example.com", // required    otp: "123456", // required});
Parameters
newEmailstringrequired

要更改为的新电子邮件地址。

otpstringrequired

发送到新邮箱的 OTP。

使用当前邮箱确认

为了增加安全性,您可以要求用户先通过发送到当前邮箱的 OTP 进行确认,再发送 OTP 到新邮箱。要启用此功能,请在插件选项中将 changeEmail.verifyCurrentEmail 设置为 true

auth.ts
import { betterAuth } from "better-auth";

export const auth = betterAuth({
    plugins: [
        emailOTP({
            changeEmail: {
                enabled: true,
                verifyCurrentEmail: true, 
            }
        })
    ]
})

在请求邮箱更改前,使用类型为 email-verificationsendVerificationOtp() 方法向用户邮箱发送 OTP。

POST/email-otp/send-verification-otp
const { data, error } = await authClient.emailOtp.sendVerificationOtp({    email: "user@example.com", // required    type: "email-verification", // required});
Parameters
emailstringrequired

发送 OTP 的电子邮件地址。

typestringrequired

OTP 的类型。必须为 email-verification,用于确认邮箱更改。

然后,用户调用 requestEmailChange() 时提供该 OTP。系统会先验证发送到当前邮箱的 OTP,然后再发送 OTP 到新邮箱。

重写默认的电子邮件验证

要重写默认的电子邮件验证,可在选项中传入 overrideDefaultEmailVerification: true。此设置会让系统改用邮件 OTP 代替默认的验证链接来完成电子邮件验证。换言之,用户将通过输入 OTP 来验证邮箱,而不是点击链接。

auth.ts
import { betterAuth } from "better-auth";
import { emailOTP } from "better-auth/plugins"

export const auth = betterAuth({
  plugins: [
    emailOTP({
      overrideDefaultEmailVerification: true, 
      async sendVerificationOTP({ email, otp, type }) {
        // 实现 sendVerificationOTP 方法,向用户电子邮件发送 OTP
      },
    }),
  ],
});

选项

  • sendVerificationOTP: 一个函数,用于向用户的电子邮件地址发送 OTP。该函数接收一个包含以下属性的对象:

    • email: 用户的电子邮件地址。
    • otp: 要发送的 OTP。
    • type: 要发送的 OTP 类型。可以是 "sign-in""email-verification""forget-password"

    建议不要等待电子邮件发送完成,以避免时序攻击。在服务器端平台上,请使用 waitUntil 或类似方法确保电子邮件已发送。

  • otpLength: OTP 的长度。默认为 6

  • expiresIn: OTP 的过期时间(秒)。默认为 300 秒。

auth.ts
import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        emailOTP({
            otpLength: 8,
            expiresIn: 600
        })
    ]
})
  • sendVerificationOnSignUp: 一个布尔值,决定是否在用户注册时发送 OTP。默认为 false

  • disableSignUp: 一个布尔值,决定是否在用户未注册时阻止自动注册。默认为 false

  • generateOTP: 生成 OTP 的函数。默认为随机 6 位数字。

  • allowedAttempts: 验证 OTP 的最大尝试次数。默认为 3。超过此限制后,OTP 将失效,用户需要请求新的 OTP。

auth.ts
import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        emailOTP({
            allowedAttempts: 5, // 允许 5 次尝试后使 OTP 失效
            expiresIn: 300
        })
    ]
})

当超过最大尝试次数时,verifyOTPsignIn.emailOtpverifyEmailresetPassword 方法将返回错误,错误码为 TOO_MANY_ATTEMPTS

  • resendStrategy: 控制当用户请求新 OTP 时已有有效 OTP 的情况。默认为 "rotate"
    • "rotate": 始终生成新的 OTP(默认行为)。
    • "reuse": 重新发送相同的 OTP 并延长其有效期。这可以防止在电子邮件延迟时同时存在多个有效代码。仅在 OTP 可恢复(plainencrypted 或自定义加密/解密)时有效。如果 OTP 是经过哈希处理的,则回退到 "rotate"。如果已达到允许的尝试次数,则会生成新的 OTP 而不是重用已耗尽的 OTP。
auth.ts
import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"

export const auth = betterAuth({
    plugins: [
        emailOTP({
            resendStrategy: "reuse", 
            async sendVerificationOTP({ email, otp, type }) {
                // 发送 OTP
            },
        })
    ]
})
  • storeOTP: 在 Better Auth 的验证层存储 OTP 之前对其进行转换的方法,可以是 encryptedhashedplain 文本。默认为 plain 文本。

注意:这不会影响发送给用户的 OTP。它只影响存储的 OTP 值。存储后端本身由全局 verification 配置控制,因此如果您配置了 secondaryStorage,这些验证记录可以存储在数据库之外。

您也可以传入自定义的加密器或哈希器来控制存储的 OTP 值的持久化方式。

自定义加密器

auth.ts
emailOTP({
    storeOTP: { 
        encrypt: async (otp) => {
            return myCustomEncryptor(otp);
        },
        decrypt: async (otp) => {
            return myCustomDecryptor(otp);
        },
    }
})

自定义哈希器

auth.ts
emailOTP({
    storeOTP: {
        hash: async (otp) => {
            return myCustomHasher(otp);
        },
    }
})