diff --git a/public/intl/messages/en-US.json b/public/intl/messages/en-US.json index f88f90959..6c5aa6bf6 100644 --- a/public/intl/messages/en-US.json +++ b/public/intl/messages/en-US.json @@ -475,6 +475,9 @@ "two-factor-require-team-description": "When enabled, all members of this team will be prompted to set up 2FA on their next login if they haven't already.", "two-factor-user-description": "Add an extra layer of security to your account. Each time you log in, you will need your password and a code from your authenticator app.", "two-factor-error-invalid-code": "Invalid verification code. Please try again.", + "two-factor-error-missing-token": "Authentication token is missing.", + "two-factor-error-invalid-partial-token": "Invalid or expired authentication token.", + "two-factor-error-invalid-backup-code": "Invalid backup code.", "two-factor-error-code-used": "This code has already been used. Wait for the next code.", "two-factor-error-too-many-attempts": "Too many failed attempts. Your account is temporarily locked.", "two-factor-error-disable-not-allowed": "Two-factor authentication is required and cannot be disabled.", diff --git a/public/intl/messages/fr-FR.json b/public/intl/messages/fr-FR.json index 792c079d6..b8d8410c5 100644 --- a/public/intl/messages/fr-FR.json +++ b/public/intl/messages/fr-FR.json @@ -473,6 +473,9 @@ "two-factor-require-team-description": "Lorsque cette option est activée, tous les membres de cette équipe seront invités à configurer l’authentification à deux facteurs lors de leur prochaine connexion s’ils ne l’ont pas déjà fait.", "two-factor-user-description": "Ajoutez une couche de sécurité supplémentaire à votre compte. À chaque connexion, vous aurez besoin de votre mot de passe et d'un code provenant de votre application d’authentification.", "two-factor-error-invalid-code": "Code de vérification invalide. Veuillez réessayer.", + "two-factor-error-missing-token": "Le jeton d'authentification est manquant.", + "two-factor-error-invalid-partial-token": "Jeton d'authentification invalide ou expiré.", + "two-factor-error-invalid-backup-code": "Code de récupération invalide.", "two-factor-error-code-used": "Ce code a déjà été utilisé. Attendez le prochain code.", "two-factor-error-too-many-attempts": "Trop de tentatives infructueuses. Votre compte est temporairement verrouillé.", "two-factor-error-disable-not-allowed": "L’authentification à deux facteurs est obligatoire et ne peut pas être désactivée.", diff --git a/src/app/api/2fa/setup/confirm/route.ts b/src/app/api/2fa/setup/confirm/route.ts index 6c202b4fb..2d03e2fa1 100644 --- a/src/app/api/2fa/setup/confirm/route.ts +++ b/src/app/api/2fa/setup/confirm/route.ts @@ -31,7 +31,7 @@ export async function POST(request: Request) { const rateCheck = await checkRateLimit(userId); if (!rateCheck.allowed) { return badRequest({ - code: 'rate-limited', + code: 'two-factor-error-too-many-attempts', message: 'Too many failed attempts', lockedUntil: rateCheck.lockedUntil, }); @@ -39,7 +39,7 @@ export async function POST(request: Request) { // Prevent OTP replay if (await isOtpReplayed(userId, token)) { - return badRequest({ code: 'otp-replayed', message: 'Code already used' }); + return badRequest({ code: 'two-factor-error-code-used', message: 'Code already used' }); } // Verify TOTP diff --git a/src/app/api/2fa/verify/route.ts b/src/app/api/2fa/verify/route.ts index 76d108d0e..619a3fd73 100644 --- a/src/app/api/2fa/verify/route.ts +++ b/src/app/api/2fa/verify/route.ts @@ -23,12 +23,12 @@ export async function POST(request: Request) { // Validate partial auth token — skip normal session auth const rawToken = getBearerToken(request); if (!rawToken) { - return unauthorized({ code: 'missing-token' }); + return unauthorized({ code: 'two-factor-error-missing-token' }); } const payload = parseSecureToken(rawToken, secret()) as any; if (!payload || payload.type !== 'partial-auth' || !payload.userId) { - return unauthorized({ code: 'invalid-partial-token' }); + return unauthorized({ code: 'two-factor-error-invalid-partial-token' }); } const { body, error } = await parseRequest(request, schema, { skipAuth: true }); @@ -54,7 +54,7 @@ export async function POST(request: Request) { return Response.json( { error: { - code: 'rate-limited', + code: 'two-factor-error-too-many-attempts', message: 'Too many failed attempts', lockedUntil: rateCheck.lockedUntil, }, @@ -72,7 +72,7 @@ export async function POST(request: Request) { if (matchIndex === null) { await recordFailedAttempt(userId); - return badRequest({ code: 'invalid-backup-code', message: 'Invalid backup code' }); + return badRequest({ code: 'two-factor-error-invalid-backup-code', message: 'Invalid backup code' }); } await prisma.client.twoFactorBackupCode.update({ @@ -84,14 +84,14 @@ export async function POST(request: Request) { const { token } = body; if (await isOtpReplayed(userId, token)) { - return badRequest({ code: 'otp-replayed', message: 'Code already used' }); + return badRequest({ code: 'two-factor-error-code-used', message: 'Code already used' }); } const decryptedSecret = decryptSecret(twoFactor.secret); if (!(await verifyTotp(token, decryptedSecret))) { await recordFailedAttempt(userId); - return badRequest({ code: 'invalid-code', message: 'Invalid verification code' }); + return badRequest({ code: 'two-factor-error-invalid-code', message: 'Invalid verification code' }); } await markOtpUsed(userId, token);