Skip to content

Commit 994eda0

Browse files
authored
Merge pull request #122 from puzed/codex/fix-otp-backup-code-reuse-vulnerability
2 parents ec07423 + afa19b5 commit 994eda0

1 file changed

Lines changed: 9 additions & 3 deletions

File tree

packages/api/src/models/otp.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { randomBytes } from "node:crypto";
22
import { verify as argonVerify, hash } from "argon2";
3-
import { and, eq } from "drizzle-orm";
3+
import { and, eq, isNull } from "drizzle-orm";
44
import { otpBackupCodes, otpConfigs, users } from "../db/schema.ts";
55
import { ValidationError } from "../errors.ts";
66
import { 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

Comments
 (0)