62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { PrismaClient } from "@prisma/client";
|
|
|
|
import { verifyPassword, signAccessToken } from "@/features/auth/authCrypto";
|
|
|
|
// /api/auth/login
|
|
// - 이메일/비밀번호를 검증해 로그인시키고,
|
|
// - JWT 액세스 토큰을 httpOnly 쿠키에 설정한 뒤, 비밀번호 해시를 제외한 유저 정보를 반환한다.
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const body = await request.json().catch(() => null as any);
|
|
|
|
const emailRaw = body?.email;
|
|
const password: string | undefined = body?.password;
|
|
|
|
if (typeof emailRaw !== "string" || typeof password !== "string") {
|
|
return NextResponse.json({ message: "email 과 password 는 필수입니다." }, { status: 400 });
|
|
}
|
|
|
|
const email = emailRaw.trim().toLowerCase();
|
|
|
|
const user = await prisma.user.findUnique({ where: { email } });
|
|
if (!user) {
|
|
return NextResponse.json({ message: "이메일 또는 비밀번호가 올바르지 않습니다." }, { status: 401 });
|
|
}
|
|
|
|
const ok = await verifyPassword(password, user.passwordHash);
|
|
if (!ok) {
|
|
return NextResponse.json({ message: "이메일 또는 비밀번호가 올바르지 않습니다." }, { status: 401 });
|
|
}
|
|
|
|
const token = await signAccessToken({
|
|
id: user.id,
|
|
email: user.email,
|
|
tokenVersion: user.tokenVersion,
|
|
emailVerified: (user as any).emailVerified ?? false,
|
|
});
|
|
|
|
const { passwordHash: _ph, ...safeUser } = user;
|
|
|
|
const res = NextResponse.json(safeUser, { status: 200 });
|
|
|
|
res.cookies.set("pb_access", token, {
|
|
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 },
|
|
);
|
|
}
|
|
}
|