File tree Expand file tree Collapse file tree 1 file changed +26
-0
lines changed
Expand file tree Collapse file tree 1 file changed +26
-0
lines changed Original file line number Diff line number Diff line change @@ -212,6 +212,32 @@ class UsersService {
212212
213213 async sendEmailOtp ( email : string ) {
214214 const normalizedEmail = email . toLowerCase ( )
215+
216+ // Check if there's already a valid OTP for this email
217+ // This prevents creating new OTPs while a valid one exists, mitigating
218+ // the attack vector where an attacker spam OTP requests
219+ // to prevent the user from logging in
220+ const existingOtp = await this . otpRepository . findOne ( {
221+ where : {
222+ email : normalizedEmail ,
223+ hashedOtp : {
224+ [ Op . regexp ] : "\\S+" , // at least one non-whitespace character (i.e. is truthy!)
225+ } ,
226+ } ,
227+ } )
228+ if ( existingOtp && existingOtp . expiresAt >= new Date ( ) ) {
229+ logger . info ( {
230+ message : "OTP request blocked: valid OTP already exists" ,
231+ meta : {
232+ email : normalizedEmail ,
233+ expiresAt : existingOtp . expiresAt ,
234+ } ,
235+ } )
236+ // Return silently to avoid revealing whether an OTP exists
237+ // This maintains security by not leaking information about existing OTPs
238+ return
239+ }
240+
215241 const { otp, hashedOtp } = await this . otpService . generateLoginOtpWithHash ( )
216242
217243 // Reset attempts to login
You can’t perform that action at this time.
0 commit comments