107 lines
3.1 KiB
TypeScript
Executable file
107 lines
3.1 KiB
TypeScript
Executable file
import { NextRequest, NextResponse } from "next/server";
|
|
import { eq, and, gt } from "drizzle-orm";
|
|
import * as schema from "@/lib/schema";
|
|
import { db } from "@/lib/db";
|
|
import { createSession, getSessionCookieOptions } from "@/lib/auth";
|
|
import { SESSION_COOKIE_NAME } from "@/lib/auth-constants";
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const contentType = request.headers.get('content-type');
|
|
if (!contentType || !contentType.includes('application/json')) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Invalid content type"
|
|
}, { status: 400 });
|
|
}
|
|
|
|
const body = await request.json();
|
|
const { userId, code } = body;
|
|
|
|
if (!userId || !code) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "User ID and code are required"
|
|
}, { status: 400 });
|
|
}
|
|
|
|
if (typeof userId !== 'string' || typeof code !== 'string') {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Invalid input format"
|
|
}, { status: 400 });
|
|
}
|
|
|
|
if (!/^\d{6}$/.test(code)) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Invalid code format"
|
|
}, { status: 400 });
|
|
}
|
|
|
|
const twoFactorRecord = await db.query.twoFactorTable.findFirst({
|
|
where: and(
|
|
eq(schema.twoFactorTable.userId, userId),
|
|
gt(schema.twoFactorTable.codeExpiresAt, new Date())
|
|
),
|
|
});
|
|
|
|
if (!twoFactorRecord) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "No valid 2FA code found or code has expired"
|
|
}, { status: 404 });
|
|
}
|
|
|
|
if (twoFactorRecord.codeAttempts >= 5) {
|
|
await db.delete(schema.twoFactorTable)
|
|
.where(eq(schema.twoFactorTable.userId, userId));
|
|
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Too many failed attempts. Please request a new code."
|
|
}, { status: 429 });
|
|
}
|
|
|
|
if (twoFactorRecord.currentCode !== code) {
|
|
await db.update(schema.twoFactorTable)
|
|
.set({
|
|
codeAttempts: twoFactorRecord.codeAttempts + 1,
|
|
updatedAt: new Date()
|
|
})
|
|
.where(eq(schema.twoFactorTable.userId, userId));
|
|
|
|
console.log(`2FA verification failed for user: ${userId}, attempts: ${twoFactorRecord.codeAttempts + 1}`);
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Invalid 2FA code"
|
|
}, { status: 401 });
|
|
}
|
|
|
|
const session = await createSession(userId);
|
|
|
|
await db.delete(schema.twoFactorTable)
|
|
.where(eq(schema.twoFactorTable.userId, userId));
|
|
|
|
console.log("2FA verification successful for user:", userId);
|
|
|
|
const response = NextResponse.json({
|
|
success: true,
|
|
message: "2FA verification successful",
|
|
redirectTo: "/account",
|
|
sessionToken: session.sessionToken
|
|
});
|
|
|
|
const cookieOptions = getSessionCookieOptions();
|
|
response.cookies.set(SESSION_COOKIE_NAME, session.sessionToken, cookieOptions);
|
|
|
|
return response;
|
|
|
|
} catch (error) {
|
|
console.error("Error in verify API:", error);
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: "Internal server error"
|
|
}, { status: 500 });
|
|
}
|
|
}
|