diff --git a/packages/better-auth/src/plugins/email-otp/email-otp.test.ts b/packages/better-auth/src/plugins/email-otp/email-otp.test.ts index e47e17d6d5..c28dfe7b14 100644 --- a/packages/better-auth/src/plugins/email-otp/email-otp.test.ts +++ b/packages/better-auth/src/plugins/email-otp/email-otp.test.ts @@ -236,6 +236,45 @@ describe("email-otp", async () => { }, }); }); + + it("should work with custom options", async () => { + const { client, testUser, auth } = await getTestInstance( + { + plugins: [ + bearer(), + emailOTP({ + async sendVerificationOTP({ email, otp: _otp, type }) { + otp = _otp; + otpFn(email, _otp, type); + }, + sendVerificationOnSignUp: true, + expiresIn: 10, + otpLength: 8, + }), + ], + emailVerification: { + autoSignInAfterVerification: true, + }, + }, + { + clientOptions: { + plugins: [emailOTPClient()], + }, + }, + ); + await client.emailOtp.sendVerificationOtp({ + type: "email-verification", + email: testUser.email, + }); + expect(otp.length).toBe(8); + vi.useFakeTimers(); + await vi.advanceTimersByTimeAsync(11 * 1000); + const verifyRes = await client.emailOtp.verifyEmail({ + email: testUser.email, + otp, + }); + expect(verifyRes.error?.code).toBe("OTP_EXPIRED"); + }); }); describe("email-otp-verify", async () => { diff --git a/packages/better-auth/src/plugins/email-otp/index.ts b/packages/better-auth/src/plugins/email-otp/index.ts index e6cf70a9e2..4d1a702f08 100644 --- a/packages/better-auth/src/plugins/email-otp/index.ts +++ b/packages/better-auth/src/plugins/email-otp/index.ts @@ -19,10 +19,14 @@ interface EmailOTPOptions { ) => Promise; /** * Length of the OTP + * + * @default 6 */ otpLength?: number; /** * Expiry time of the OTP in seconds + * + * @default 300 (5 minutes) */ expiresIn?: number; /** @@ -44,7 +48,7 @@ const types = ["email-verification", "sign-in", "forget-password"] as const; export const emailOTP = (options: EmailOTPOptions) => { const opts = { - expireIn: 5 * 60, + expiresIn: 5 * 60, otpLength: 6, ...options, }; @@ -113,7 +117,7 @@ export const emailOTP = (options: EmailOTPOptions) => { .createVerificationValue({ value: otp, identifier: `${ctx.body.type}-otp-${email}`, - expiresAt: getDate(opts.expireIn, "sec"), + expiresAt: getDate(opts.expiresIn, "sec"), }) .catch(async (error) => { // might be duplicate key error @@ -124,7 +128,7 @@ export const emailOTP = (options: EmailOTPOptions) => { await ctx.context.internalAdapter.createVerificationValue({ value: otp, identifier: `${ctx.body.type}-otp-${email}`, - expiresAt: getDate(opts.expireIn, "sec"), + expiresAt: getDate(opts.expiresIn, "sec"), }); }); await options.sendVerificationOTP( @@ -177,7 +181,7 @@ export const emailOTP = (options: EmailOTPOptions) => { await ctx.context.internalAdapter.createVerificationValue({ value: otp, identifier: `${ctx.body.type}-otp-${email}`, - expiresAt: getDate(opts.expireIn, "sec"), + expiresAt: getDate(opts.expiresIn, "sec"), }); return otp; }, @@ -493,7 +497,7 @@ export const emailOTP = (options: EmailOTPOptions) => { await ctx.context.internalAdapter.createVerificationValue({ value: otp, identifier: `forget-password-otp-${email}`, - expiresAt: getDate(opts.expireIn, "sec"), + expiresAt: getDate(opts.expiresIn, "sec"), }); await options.sendVerificationOTP( { @@ -638,7 +642,7 @@ export const emailOTP = (options: EmailOTPOptions) => { await ctx.context.internalAdapter.createVerificationValue({ value: otp, identifier: `email-verification-otp-${email}`, - expiresAt: getDate(opts.expireIn, "sec"), + expiresAt: getDate(opts.expiresIn, "sec"), }); await options.sendVerificationOTP( {