import { NextResponse } from "next/server"; import { createHash } from "crypto"; import { getServiceSupabase } from "@/lib/supabase/client"; export const runtime = "nodejs"; function hashToken(token: string): string { return createHash("sha256").update(token).digest("hex"); } export async function POST(request: Request) { try { const body = (await request.json()) as { token?: string; email?: string; password?: string; }; const token = body.token || ""; const email = (body.email || "").trim().toLowerCase(); const password = body.password || ""; if (!token || !email || !password) { return NextResponse.json( { error: "Token, email, and password are required" }, { status: 400 } ); } if (password.length < 8) { return NextResponse.json( { error: "Password must be at least 8 characters" }, { status: 400 } ); } const supabase = getServiceSupabase(); const tokenHash = hashToken(token); const now = new Date().toISOString(); // Find valid token with user info const { data: resetToken } = await supabase .from("password_reset_tokens") .select("id, user_id, users(email, name)") .eq("token_hash", tokenHash) .eq("used", false) .gt("expires_at", now) .maybeSingle(); if (!resetToken) { return NextResponse.json( { error: "Invalid or expired reset token" }, { status: 400 } ); } // Get user email from the nested users object const userEmail = Array.isArray(resetToken.users) ? resetToken.users[0]?.email : resetToken.users?.email; const userName = Array.isArray(resetToken.users) ? resetToken.users[0]?.name : resetToken.users?.name; if (userEmail?.toLowerCase() !== email) { return NextResponse.json({ error: "Invalid reset token" }, { status: 400 }); } // Update Supabase Auth password. If auth user doesn't exist yet (legacy migration), // create it with the same UUID so app foreign keys remain valid. const { error: updateError } = await supabase.auth.admin.updateUserById(resetToken.user_id, { password, }); if (updateError) { const updateMessage = updateError.message || ""; if (updateMessage.toLowerCase().includes("not found")) { const { error: createError } = await supabase.auth.admin.createUser({ id: resetToken.user_id, email, password, email_confirm: true, user_metadata: { name: typeof userName === "string" && userName.trim().length > 0 ? userName : undefined, }, }); if (createError) throw createError; } else { throw updateError; } } // Mark token as used await supabase .from("password_reset_tokens") .update({ used: true }) .eq("id", resetToken.id); // Delete all sessions for this user (force re-login) await supabase.from("sessions").delete().eq("user_id", resetToken.user_id); return NextResponse.json({ success: true, message: "Password reset successfully", }); } catch (error) { console.error("Reset password error:", error); return NextResponse.json({ error: "Failed to reset password" }, { status: 500 }); } }