From fc32e39ac0de8a4a2bd0056dc0bf587f35b5ce99 Mon Sep 17 00:00:00 2001 From: "Rudy B." Date: Sat, 25 Apr 2026 17:56:56 +0200 Subject: [PATCH] fix(2FA): return `429` when too many failed attemps --- src/app/api/2fa/disable/route.ts | 15 ++++++++++----- src/app/api/2fa/setup/confirm/route.ts | 15 ++++++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/app/api/2fa/disable/route.ts b/src/app/api/2fa/disable/route.ts index 667cc3ceb..859deac6d 100644 --- a/src/app/api/2fa/disable/route.ts +++ b/src/app/api/2fa/disable/route.ts @@ -73,11 +73,16 @@ export async function POST(request: Request) { // Verify rate limit const rateCheck = await checkRateLimit(userId); if (!rateCheck.allowed) { - return badRequest({ - code: 'two-factor-error-too-many-attempts', - message: 'Too many failed attempts', - lockedUntil: rateCheck.lockedUntil, - }); + return Response.json( + { + error: { + code: 'two-factor-error-too-many-attempts', + message: 'Too many failed attempts', + lockedUntil: rateCheck.lockedUntil, + }, + }, + { status: 429 }, + ); } // Prevent OTP replay diff --git a/src/app/api/2fa/setup/confirm/route.ts b/src/app/api/2fa/setup/confirm/route.ts index 629a15c86..e64e558c3 100644 --- a/src/app/api/2fa/setup/confirm/route.ts +++ b/src/app/api/2fa/setup/confirm/route.ts @@ -33,11 +33,16 @@ export async function POST(request: Request) { // Verify rate limit const rateCheck = await checkRateLimit(userId); if (!rateCheck.allowed) { - return badRequest({ - code: 'two-factor-error-too-many-attempts', - message: 'Too many failed attempts', - lockedUntil: rateCheck.lockedUntil, - }); + return Response.json( + { + error: { + code: 'two-factor-error-too-many-attempts', + message: 'Too many failed attempts', + lockedUntil: rateCheck.lockedUntil, + }, + }, + { status: 429 }, + ); } // Prevent OTP replay