11import { randomBytes } from "node:crypto" ;
22import { verify as argonVerify , hash } from "argon2" ;
3- import { and , eq } from "drizzle-orm" ;
3+ import { and , eq , isNull } from "drizzle-orm" ;
44import { otpBackupCodes , otpConfigs , users } from "../db/schema.ts" ;
55import { ValidationError } from "../errors.ts" ;
66import { getSetting } from "../services/settings.ts" ;
@@ -184,7 +184,13 @@ export async function verifyOtpCode(
184184 const backupCodes = await context . db
185185 . select ( { id : otpBackupCodes . id , codeHash : otpBackupCodes . codeHash } )
186186 . from ( otpBackupCodes )
187- . where ( and ( eq ( otpBackupCodes . cohort , cohort ) , eq ( otpBackupCodes . subjectId , subjectId ) ) ) ;
187+ . where (
188+ and (
189+ eq ( otpBackupCodes . cohort , cohort ) ,
190+ eq ( otpBackupCodes . subjectId , subjectId ) ,
191+ isNull ( otpBackupCodes . usedAt )
192+ )
193+ ) ;
188194 for ( const backupCode of backupCodes ) {
189195 const isMatch = await argonVerify ( backupCode . codeHash , code ) . catch ( ( ) => false ) ;
190196 if ( isMatch ) {
@@ -200,7 +206,7 @@ export async function verifyOtpCode(
200206 await context . db
201207 . update ( otpBackupCodes )
202208 . set ( { usedAt : new Date ( ) } )
203- . where ( eq ( otpBackupCodes . id , backupCode . id ) ) ;
209+ . where ( and ( eq ( otpBackupCodes . id , backupCode . id ) , isNull ( otpBackupCodes . usedAt ) ) ) ;
204210 return { success : true } ;
205211 }
206212 }
0 commit comments