133 lines
3.7 KiB
TypeScript
133 lines
3.7 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { decodeJwt } from "jose";
|
|
|
|
const BE_BASE_URL = process.env.BE_BASE_URL || "http://localhost:8583";
|
|
const COOKIE_NAME = "auth_token";
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const { email, currentPassword, newPassword } = await request.json();
|
|
|
|
// Get the auth token from cookies first
|
|
const { cookies } = await import("next/headers");
|
|
const cookieStore = cookies();
|
|
const token = (await cookieStore).get(COOKIE_NAME)?.value;
|
|
|
|
if (!token) {
|
|
return NextResponse.json(
|
|
{ success: false, message: "No authentication token found" },
|
|
{ status: 401 }
|
|
);
|
|
}
|
|
|
|
// Check MustChangePassword flag from current token
|
|
let mustChangePassword = false;
|
|
try {
|
|
const payload = decodeJwt(token);
|
|
mustChangePassword = payload.MustChangePassword || false;
|
|
console.log(
|
|
"🔍 Current JWT MustChangePassword flag:",
|
|
mustChangePassword
|
|
);
|
|
} catch (err) {
|
|
console.error("❌ Failed to decode current JWT:", err);
|
|
}
|
|
|
|
// Skip currentPassword validation if MustChangePassword is true
|
|
if (!email || !newPassword || (!mustChangePassword && !currentPassword)) {
|
|
return NextResponse.json(
|
|
{
|
|
success: false,
|
|
message: "Email, current password, and new password are required",
|
|
},
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// 🔁 Call backend API with the correct payload
|
|
const requestBody: {
|
|
email: string;
|
|
new_password: string;
|
|
current_password?: string;
|
|
} = {
|
|
email,
|
|
new_password: newPassword,
|
|
};
|
|
|
|
// Only include current_password if MustChangePassword is false
|
|
if (!mustChangePassword && currentPassword) {
|
|
requestBody.current_password = currentPassword;
|
|
}
|
|
|
|
const resp = await fetch(`${BE_BASE_URL}/api/v1/auth/change-password`, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
Authorization: `Bearer ${token}`,
|
|
},
|
|
body: JSON.stringify(requestBody),
|
|
});
|
|
|
|
const data = await resp.json();
|
|
|
|
if (!resp.ok) {
|
|
console.log("[DEBUG] [CHANGE-PASSWORD] Error response:", {
|
|
status: resp.status,
|
|
data,
|
|
});
|
|
return NextResponse.json(
|
|
{ success: false, message: data?.message || "Password change failed" },
|
|
{ status: resp.status }
|
|
);
|
|
}
|
|
|
|
// ✅ Handle new token from backend
|
|
const newToken = data?.token;
|
|
const response = NextResponse.json({
|
|
success: true,
|
|
message: data?.message || "Password changed successfully",
|
|
});
|
|
|
|
if (newToken) {
|
|
try {
|
|
const payload = decodeJwt(newToken);
|
|
console.log("🔍 New JWT payload:", payload);
|
|
console.log(
|
|
"🔍 must_change_password flag:",
|
|
payload.must_change_password
|
|
);
|
|
} catch (err) {
|
|
console.error("❌ Failed to decode new JWT:", err);
|
|
}
|
|
|
|
// Derive maxAge from JWT exp if available; fallback to 12h
|
|
let maxAge = 60 * 60 * 12;
|
|
try {
|
|
const payload = decodeJwt(newToken);
|
|
if (payload?.exp) {
|
|
const secondsLeft = payload.exp - Math.floor(Date.now() / 1000);
|
|
if (secondsLeft > 0) maxAge = secondsLeft;
|
|
}
|
|
} catch {}
|
|
|
|
response.cookies.set({
|
|
name: COOKIE_NAME,
|
|
value: newToken,
|
|
httpOnly: true,
|
|
secure: process.env.NODE_ENV === "production",
|
|
sameSite: "lax",
|
|
path: "/",
|
|
maxAge,
|
|
});
|
|
}
|
|
|
|
return response;
|
|
} catch (error) {
|
|
console.error("❌ Change password proxy error:", error);
|
|
return NextResponse.json(
|
|
{ success: false, message: "Internal server error" },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|