Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); fix: email OTP error codes and docs (#845) · better-auth/better-auth@e91be5a · GitHub
Skip to content

Commit e91be5a

Browse files
authored
fix: email OTP error codes and docs (#845)
1 parent 36e2ee2 commit e91be5a

3 files changed

Lines changed: 67 additions & 45 deletions

File tree

‎docs/content/docs/guides/your-first-plugin.mdx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,16 @@ import { createAuthMiddleware } from "better-auth/plugins";
127127
//...
128128
handler: createAuthMiddleware(async (ctx) => {
129129
const { birthday } =ctx.body;
130-
if(!birthdayinstanceofDate) throwAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
130+
if(!birthdayinstanceofDate) {
131+
thrownewAPIError("BAD_REQUEST", { message: "Birthday must be of type Date." });
132+
}
131133

132134
const today =newDate();
133135
const fiveYearsAgo =newDate(today.setFullYear(today.getFullYear() -5));
134136

135-
if(birthday<=fiveYearsAgo) throwAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
137+
if(birthday<=fiveYearsAgo) {
138+
thrownewAPIError("BAD_REQUEST", { message: "User must be above 5 years old." });
139+
}
136140

137141
return { context: ctx };
138142
}),

‎docs/content/docs/plugins/email-otp.mdx‎

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: Email OTP
33
description: Email OTP plugin for Better Auth.
44
---
55

6-
The Email OTP plugin allows user to sign-in and verify their email using a one-time password (OTP) sent to their email address.
6+
The Email OTP plugin allows user to sign-in, verify their email, or reset their password using a one-time password (OTP) sent to their email address.
77

88

99
## Installation
@@ -50,12 +50,12 @@ The Email OTP plugin allows user to sign-in and verify their email using a one-t
5050

5151
### Send OTP
5252

53-
Before signing in or verifying email, you need to send an OTP to the user's email address.
53+
First, send an OTP to the user's email address.
5454

5555
```ts title="example.ts"
5656
awaitauthClient.emailOtp.sendVerificationOtp({
5757
email: "user-email@email.com",
58-
type: "sign-in"// or "email-verification"
58+
type: "sign-in"// or "email-verification", "forget-password"
5959
})
6060
```
6161

@@ -70,8 +70,7 @@ const user = await authClient.signIn.emailOtp({
7070
})
7171
```
7272

73-
If the user is not registered, it'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
74-
73+
If the user is not registered, they'll be automatically registered. If you want to prevent this, you can pass `disableSignUp` as `true` in the options.
7574

7675
### Verify Email
7776

@@ -84,12 +83,24 @@ const user = await authClient.emailOtp.verifyEmail({
8483
})
8584
```
8685

86+
### Reset Password
87+
88+
To reset the user's password, use the `resetPassword()` method.
89+
90+
```ts title="example.ts"
91+
awaitauthClient.emailOtp.resetPassword({
92+
email: "user-email@email.com",
93+
otp: "123456",
94+
password: "password"
95+
})
96+
```
97+
8798
## Options
8899

89100
-`sendVerificationOTP`: A function that sends the OTP to the user's email address. The function receives an object with the following properties:
90101
-`email`: The user's email address.
91102
-`otp`: The OTP to send.
92-
-`type`: The type of OTP to send. Can be either "sign-in" or "email-verification".
103+
-`type`: The type of OTP to send. Can be "sign-in", "email-verification", or "forget-password".
93104

94105
### Example
95106

@@ -104,19 +115,21 @@ export const auth = betterAuth({
104115
otp,
105116
type
106117
}) {
107-
if(type==="sign-in") {
118+
if(type==="sign-in") {
108119
// Send the OTP for sign-in
109-
} else {
120+
} elseif (type==="email-verification") {
110121
// Send the OTP for email verification
122+
} else {
123+
// Send the OTP for password reset
111124
}
112125
},
113126
})
114127
]
115128
})
116129
```
117130

118-
-`otpLength`: The length of the OTP. Defaults to 6.
119-
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to 300 seconds.
131+
-`otpLength`: The length of the OTP. Defaults to `6`.
132+
-`otpExpiry`: The expiry time of the OTP in seconds. Defaults to `300` seconds.
120133

121134
```ts title="auth.ts"
122135
import { betterAuth } from"better-auth"
@@ -131,6 +144,6 @@ export const auth = betterAuth({
131144
})
132145
```
133146

134-
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to false.
147+
-`sendVerificationOnSignUp`: A boolean value that determines whether to send the OTP when a user signs up. Defaults to `false`.
135148

136-
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to false.
149+
-`disableSignUp`: A boolean value that determines whether to prevent automatic sign-up when the user is not registered. Defaults to `false`.

‎packages/better-auth/src/plugins/email-otp/index.ts‎

Lines changed: 36 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import{INVALID,z}from"zod";
1+
import{z}from"zod";
22
import{APIError,createAuthEndpoint}from"../../api";
33
importtype{BetterAuthPlugin,User}from"../../types";
44
import{alphabet,generateRandomString}from"../../crypto";
55
import{getDate}from"../../utils/date";
66
import{setSessionCookie}from"../../cookies";
7-
import{getEndpointResponse}from"../../utils/plugin-helper";
87

98
interfaceEmailOTPOptions{
109
/**
@@ -273,30 +272,30 @@ export const emailOTP = (options: EmailOTPOptions) => {
273272
constemailRegex=/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
274273
if(!emailRegex.test(email)){
275274
thrownewAPIError("BAD_REQUEST",{
276-
message: "Invalid email",
275+
message: ERROR_CODES.INVALID_EMAIL,
277276
});
278277
}
279278
constverificationValue=
280279
awaitctx.context.internalAdapter.findVerificationValue(
281280
`email-verification-otp-${email}`,
282281
);
283-
if(!verificationValue||verificationValue.expiresAt<newDate()){
284-
if(verificationValue){
285-
awaitctx.context.internalAdapter.deleteVerificationValue(
286-
verificationValue.id,
287-
);
288-
thrownewAPIError("BAD_REQUEST",{
289-
message: "OTP expired",
290-
});
291-
}
282+
if(!verificationValue){
292283
thrownewAPIError("BAD_REQUEST",{
293-
message: "Invalid OTP",
284+
message: ERROR_CODES.INVALID_OTP,
285+
});
286+
}
287+
if(verificationValue.expiresAt<newDate()){
288+
awaitctx.context.internalAdapter.deleteVerificationValue(
289+
verificationValue.id,
290+
);
291+
thrownewAPIError("BAD_REQUEST",{
292+
message: ERROR_CODES.OTP_EXPIRED,
294293
});
295294
}
296295
constotp=ctx.body.otp;
297296
if(verificationValue.value!==otp){
298297
thrownewAPIError("BAD_REQUEST",{
299-
message: "Invalid OTP",
298+
message: ERROR_CODES.INVALID_OTP,
300299
});
301300
}
302301
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -305,7 +304,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
305304
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
306305
if(!user){
307306
thrownewAPIError("BAD_REQUEST",{
308-
message: "User not found",
307+
message: ERROR_CODES.USER_NOT_FOUND,
309308
});
310309
}
311310
constupdatedUser=awaitctx.context.internalAdapter.updateUser(
@@ -370,20 +369,23 @@ export const emailOTP = (options: EmailOTPOptions) => {
370369
awaitctx.context.internalAdapter.findVerificationValue(
371370
`sign-in-otp-${email}`,
372371
);
373-
if(!verificationValue||verificationValue.expiresAt<newDate()){
374-
if(verificationValue){
375-
awaitctx.context.internalAdapter.deleteVerificationValue(
376-
verificationValue.id,
377-
);
378-
}
372+
if(!verificationValue){
379373
thrownewAPIError("BAD_REQUEST",{
380-
message: "Invalid OTP",
374+
message: ERROR_CODES.INVALID_OTP,
375+
});
376+
}
377+
if(verificationValue.expiresAt<newDate()){
378+
awaitctx.context.internalAdapter.deleteVerificationValue(
379+
verificationValue.id,
380+
);
381+
thrownewAPIError("BAD_REQUEST",{
382+
message: ERROR_CODES.OTP_EXPIRED,
381383
});
382384
}
383385
constotp=ctx.body.otp;
384386
if(verificationValue.value!==otp){
385387
thrownewAPIError("BAD_REQUEST",{
386-
message: "Invalid OTP",
388+
message: ERROR_CODES.INVALID_OTP,
387389
});
388390
}
389391
awaitctx.context.internalAdapter.deleteVerificationValue(
@@ -393,7 +395,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
393395
if(!user){
394396
if(opts.disableSignUp){
395397
thrownewAPIError("BAD_REQUEST",{
396-
message: "User not found",
398+
message: ERROR_CODES.USER_NOT_FOUND,
397399
});
398400
}
399401
constnewUser=awaitctx.context.internalAdapter.createUser({
@@ -472,7 +474,7 @@ export const emailOTP = (options: EmailOTPOptions) => {
472474
constuser=awaitctx.context.internalAdapter.findUserByEmail(email);
473475
if(!user){
474476
thrownewAPIError("BAD_REQUEST",{
475-
message: "User not found",
477+
message: ERROR_CODES.USER_NOT_FOUND,
476478
});
477479
}
478480
constotp=generateRandomString(opts.otpLength,alphabet("0-9"));
@@ -544,12 +546,15 @@ export const emailOTP = (options: EmailOTPOptions) => {
544546
awaitctx.context.internalAdapter.findVerificationValue(
545547
`forget-password-otp-${email}`,
546548
);
547-
if(!verificationValue||verificationValue.expiresAt<newDate()){
548-
if(verificationValue){
549-
awaitctx.context.internalAdapter.deleteVerificationValue(
550-
verificationValue.id,
551-
);
552-
}
549+
if(!verificationValue){
550+
thrownewAPIError("BAD_REQUEST",{
551+
message: ERROR_CODES.INVALID_OTP,
552+
});
553+
}
554+
if(verificationValue.expiresAt<newDate()){
555+
awaitctx.context.internalAdapter.deleteVerificationValue(
556+
verificationValue.id,
557+
);
553558
thrownewAPIError("BAD_REQUEST",{
554559
message: ERROR_CODES.OTP_EXPIRED,
555560
});

0 commit comments

Comments
 (0)