import { NextResponse } from "next/server"; import { PrismaClient } from "@prisma/client"; import { verifyPassword, hashPassword, signAccessToken, verifyAccessToken } from "@/features/auth/authCrypto"; // /api/auth/change-password // - pb_access JWT 쿠키로 인증된 사용자의 비밀번호를 변경한다. // - 현재 비밀번호를 한 번 더 입력받아 검증한 뒤, 새 비밀번호로 passwordHash 를 교체하고 tokenVersion 을 증가시킨다. const prisma = new PrismaClient(); function extractTokenFromCookieHeader(cookieHeader: string | null): string | null { if (!cookieHeader) return null; const parts = cookieHeader.split(";"); for (const part of parts) { const trimmed = part.trim(); if (trimmed.startsWith("pb_access=")) { const value = trimmed.substring("pb_access=".length); return decodeURIComponent(value); } } return null; } export async function POST(request: Request) { try { const cookieHeader = request.headers.get("cookie"); const token = extractTokenFromCookieHeader(cookieHeader); if (!token) { return NextResponse.json({ message: "인증 정보가 없습니다." }, { status: 401 }); } const payload = await verifyAccessToken(token); if (!payload) { return NextResponse.json({ message: "유효하지 않은 토큰입니다." }, { status: 401 }); } const body = (await request.json().catch(() => null)) as any; const currentPassword: string | undefined = body?.currentPassword; const newPassword: string | undefined = body?.newPassword; if (typeof currentPassword !== "string" || typeof newPassword !== "string") { return NextResponse.json({ message: "currentPassword 와 newPassword 는 필수입니다." }, { status: 400 }); } const user = await prisma.user.findUnique({ where: { id: payload.sub } }); if (!user) { return NextResponse.json({ message: "사용자를 찾을 수 없습니다." }, { status: 401 }); } const ok = await verifyPassword(currentPassword, (user as any).passwordHash); if (!ok) { return NextResponse.json({ message: "현재 비밀번호가 일치하지 않습니다." }, { status: 401 }); } if (newPassword.length < 8) { return NextResponse.json({ message: "비밀번호는 최소 8자 이상이어야 합니다." }, { status: 400 }); } const newHash = await hashPassword(newPassword); const updated = await prisma.user.update({ where: { id: user.id }, data: { passwordHash: newHash, tokenVersion: (user as any).tokenVersion + 1, }, }); const newToken = await signAccessToken({ id: updated.id, email: (updated as any).email, tokenVersion: (updated as any).tokenVersion, emailVerified: (updated as any).emailVerified ?? false, }); const res = NextResponse.json({ message: "비밀번호가 변경되었습니다." }, { status: 200 }); res.cookies.set("pb_access", newToken, { httpOnly: true, secure: true, sameSite: "lax", path: "/", maxAge: 7 * 24 * 60 * 60, }); return res; } catch (error: any) { return NextResponse.json( { message: "비밀번호 변경 중 오류가 발생했습니다.", error: error?.message }, { status: 500 }, ); } }