电子邮件 OTP
用于 Better Auth 的电子邮件 OTP 插件。
电子邮件 OTP 插件允许用户通过发送到其电子邮件地址的一次性密码(OTP)进行登录、验证电子邮件或重置密码。
安装
将插件添加到您的身份验证配置中
将 emailOTP 插件添加到您的身份验证配置,并实现 sendVerificationOTP() 方法。
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
}
},
})
]
})添加客户端插件
import { createAuthClient } from "better-auth/client"
import { emailOTPClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
emailOTPClient()
]
})使用方法
发送 OTP
使用 sendVerificationOtp() 方法向用户的电子邮件地址发送 OTP。
const { data, error } = await authClient.emailOtp.sendVerificationOtp({ email: "user@example.com", // required type: "sign-in", // required});emailstringrequiredEmail address to send the OTP.
type"email-verification" | "sign-in" | "forget-password"requiredType of the OTP. sign-in, email-verification, or forget-password.
校验 OTP(可选)
使用 checkVerificationOtp() 方法验证 OTP 是否有效。
const { data, error } = await authClient.emailOtp.checkVerificationOtp({ email: "user@example.com", // required type: "sign-in", // required otp: "123456", // required});emailstringrequiredEmail address to send the OTP.
type"email-verification" | "sign-in" | "forget-password"requiredType of the OTP. sign-in, email-verification, or forget-password.
otpstringrequiredOTP sent to the email.
使用 OTP 登录
使用 OTP 登录时,先通过 sendVerificationOtp() 方法向用户电子邮件发送类型为 "sign-in" 的 OTP。
const { data, error } = await authClient.emailOtp.sendVerificationOtp({ email: "user@example.com", // required type: "sign-in", // required});emailstringrequiredEmail address to send the OTP.
type"sign-in"requiredType of the OTP.
用户提供 OTP 后,可使用 signIn.emailOtp() 方法登录用户。
const { data, error } = await authClient.signIn.emailOtp({ email: "user@example.com", // required otp: "123456", // required name: "John Doe", image: "https://example.com/image.png",});emailstringrequiredEmail address to sign in.
otpstringrequiredOTP sent to the email.
namestringUser display name. Only used when the user is registering for the first time.
imagestringUser profile image URL. Only used when the user is registering for the first time.
If the user is not registered, they'll be automatically registered. Configured additional fields are also accepted for new users. To prevent automatic sign-up, pass disableSignUp as true in the options.
使用 OTP 验证电子邮件
使用 OTP 验证用户邮箱时,先通过 sendVerificationOtp() 方法向用户邮箱发送类型为 "email-verification" 的 OTP。
const { data, error } = await authClient.emailOtp.sendVerificationOtp({ email: "user@example.com", // required type: "email-verification", // required});emailstringrequiredEmail address to send the OTP.
type"email-verification"requiredType of the OTP.
用户输入 OTP 后,使用 verifyEmail() 方法完成邮箱验证。
const { data, error } = await authClient.emailOtp.verifyEmail({ email: "user@example.com", // required otp: "123456", // required});emailstringrequiredEmail address to verify.
otpstringrequiredOTP to verify.
使用 OTP 重置密码
使用 OTP 重置密码时,使用 emailOtp.requestPasswordReset() 方法向用户邮箱发送类型为 "forget-password" 的 OTP。
const { data, error } = await authClient.emailOtp.requestPasswordReset({ email: "user@example.com", // required});emailstringrequiredEmail address to send the OTP.
The /forget-password/email-otp endpoint is deprecated. Please use /email-otp/request-password-reset instead.
用户提供 OTP 后,可以选择使用 checkVerificationOtp() 方法验证 OTP 是否有效。
const { data, error } = await authClient.emailOtp.checkVerificationOtp({ email: "user@example.com", // required type: "forget-password", // required otp: "123456", // required});emailstringrequiredEmail address to send the OTP.
type"forget-password"requiredType of the OTP.
otpstringrequiredOTP sent to the email.
然后,使用 resetPassword() 方法重置用户密码。
const { data, error } = await authClient.emailOtp.resetPassword({ email: "user@example.com", // required otp: "123456", // required password: "new-secure-password", // required});emailstringrequiredEmail address to reset the password.
otpstringrequiredOTP sent to the email.
passwordstringrequiredNew password.
使用 OTP 更改电子邮件
要允许用户使用 OTP 更改电子邮件,需先启用 changeEmail 功能,该功能默认关闭。将 changeEmail.enabled 设置为 true:
import { betterAuth } from "better-auth";
export const auth = betterAuth({
plugins: [
emailOTP({
changeEmail: {
enabled: true,
}
})
]
})默认情况下,当用户请求更改电子邮件时,OTP 会发送到新的电子邮件地址。只有用户验证了新邮箱后,邮箱才会被更新。
使用方法
要更改用户电子邮件地址,使用 emailOtp.requestEmailChange() 方法向用户新的电子邮件地址发送类型为 "change-email" 的 OTP。
const { data, error } = await authClient.emailOtp.requestEmailChange({ newEmail: "user@example.com", // required otp: "123456",});newEmailstringrequiredNew email address to send the OTP.
otpstringOTP sent to the current email. This is required when the changeEmail.verifyCurrentEmail option is set to true.
用户提供 OTP 后,使用 changeEmail() 方法更改用户邮箱地址。
const { data, error } = await authClient.emailOtp.changeEmail({ newEmail: "user@example.com", // required otp: "123456", // required});newEmailstringrequiredNew email address to change to.
otpstringrequiredOTP sent to the new email.
使用当前邮箱确认
为了增加安全性,您可以要求用户先通过发送到当前邮箱的 OTP 进行确认,再发送 OTP 到新邮箱。要启用此功能,请在插件选项中将 changeEmail.verifyCurrentEmail 设置为 true。
import { betterAuth } from "better-auth";
export const auth = betterAuth({
plugins: [
emailOTP({
changeEmail: {
enabled: true,
verifyCurrentEmail: true,
}
})
]
})在请求邮箱更改前,使用类型为 email-verification 的 sendVerificationOtp() 方法向用户邮箱发送 OTP。
const { data, error } = await authClient.emailOtp.sendVerificationOtp({ email: "user@example.com", // required type: "email-verification", // required});emailstringrequiredEmail address to send the OTP.
typestringrequiredType of the OTP. Must be email-verification for confirming email change.
然后,用户调用 requestEmailChange() 时提供该 OTP。系统会先验证发送到当前邮箱的 OTP,然后再发送 OTP 到新邮箱。
重写默认的电子邮件验证
要重写默认的电子邮件验证,可在选项中传入 overrideDefaultEmailVerification: true。此设置会让系统改用邮件 OTP 代替默认的验证链接来完成电子邮件验证。换言之,用户将通过输入 OTP 来验证邮箱,而不是点击链接。
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秒。
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。
import { betterAuth } from "better-auth"
import { emailOTP } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
emailOTP({
allowedAttempts: 5, // 允许 5 次尝试后使 OTP 失效
expiresIn: 300
})
]
})当超过最大尝试次数时,verifyOTP、signIn.emailOtp、verifyEmail 和 resetPassword 方法将返回错误,错误码为 TOO_MANY_ATTEMPTS。
resendStrategy: 控制当用户请求新 OTP 时已有有效 OTP 的情况。默认为"rotate"。"rotate": 始终生成新的 OTP(默认行为)。"reuse": 重新发送相同的 OTP 并延长其有效期。这可以防止在电子邮件延迟时同时存在多个有效代码。仅在 OTP 可恢复(plain、encrypted或自定义加密/解密)时有效。如果 OTP 是经过哈希处理的,则回退到"rotate"。如果已达到允许的尝试次数,则会生成新的 OTP 而不是重用已耗尽的 OTP。
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 之前对其进行转换的方法,可以是encrypted、hashed或plain文本。默认为plain文本。
注意:这不会影响发送给用户的 OTP。它只影响存储的 OTP 值。存储后端本身由全局 verification 配置控制,因此如果您配置了 secondaryStorage,这些验证记录可以存储在数据库之外。
您也可以传入自定义的加密器或哈希器来控制存储的 OTP 值的持久化方式。
自定义加密器
emailOTP({
storeOTP: {
encrypt: async (otp) => {
return myCustomEncryptor(otp);
},
decrypt: async (otp) => {
return myCustomDecryptor(otp);
},
}
})自定义哈希器
emailOTP({
storeOTP: {
hash: async (otp) => {
return myCustomHasher(otp);
},
}
})