마이페이지, 리포트, 메일인증
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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 },
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user