diff --git a/app/api/account/password/route.ts b/app/api/account/password/route.ts index 474fe2a4..046584a6 100644 --- a/app/api/account/password/route.ts +++ b/app/api/account/password/route.ts @@ -1,6 +1,7 @@ // app/api/account/password/route.ts import { NextResponse } from "next/server"; import { requireBearer } from "@/app/api/_lib/auth"; +import { loginDirectus } from "@/lib/directus"; export const runtime = "nodejs"; @@ -17,19 +18,29 @@ async function handle(req: Request) { if (!current || !next) return bad("Missing current and/or new password"); if (next.length < 8) return bad("Password must be at least 8 characters"); - // 1) Fetch user provider; block with clear message if not local - const who = await fetch(`${API}/users/me?fields=id,provider,email`, { + // 1) Load the current user to get email + provider + const meRes = await fetch(`${API}/users/me?fields=id,email,provider`, { headers: { Authorization: `Bearer ${bearer}`, Accept: "application/json" }, cache: "no-store", }); - const whoJson = await who.json().catch(() => ({})); - if (!who.ok) { + const me = await meRes.json().catch(() => ({})); + if (!meRes.ok) { return NextResponse.json( - { error: "Could not verify user", debug: whoJson?.errors?.[0]?.message || who.statusText }, - { status: who.status } + { error: "Could not verify user", debug: me?.errors?.[0]?.message || meRes.statusText }, + { status: meRes.status } ); } - const provider = whoJson?.data?.provider ?? whoJson?.provider ?? "local"; + + const email: string | undefined = me?.data?.email ?? me?.email; + const provider: string = me?.data?.provider ?? me?.provider ?? "local"; + + if (!email) { + return NextResponse.json( + { error: "User email not available", debug: "users/me returned no email" }, + { status: 400 } + ); + } + if (provider !== "local") { return NextResponse.json( { error: "Password managed by external provider", debug: `provider=${provider}` }, @@ -37,34 +48,30 @@ async function handle(req: Request) { ); } - // 2) Send both "old_password" and "current_password" for cross-version compatibility - const payload = { password: next, old_password: current, current_password: current }; + // 2) Verify the CURRENT password by logging in to Directus + const auth = await loginDirectus(email, current).catch(() => null); + const access = auth?.access_token ?? auth?.data?.access_token; + if (!access) { + // We’ve confirmed the “current” really doesn’t match + return NextResponse.json({ error: "Current password is incorrect" }, { status: 401 }); + } - const res = await fetch(`${API}/users/me`, { + // 3) Update password using ONLY the 'password' field (avoid non-existent keys) + const patchRes = await fetch(`${API}/users/me`, { method: "PATCH", headers: { Authorization: `Bearer ${bearer}`, "Content-Type": "application/json", Accept: "application/json", }, - body: JSON.stringify(payload), + body: JSON.stringify({ password: next }), }); - const j = await res.json().catch(() => ({})); - - if (!res.ok) { + const j = await patchRes.json().catch(() => ({})); + if (!patchRes.ok) { const reason = - j?.errors?.[0]?.message || - j?.error || - (typeof j === "string" ? j : "") || - "Password change failed"; - - // Only show the friendly message when it truly looks like a wrong-current-password case. - const friendly = res.status === 401 && /old_password|current_password|credential|invalid/i.test(reason) - ? "Current password is incorrect" - : reason; - - return NextResponse.json({ error: friendly, debug: reason }, { status: res.status }); + j?.errors?.[0]?.message || j?.error || (typeof j === "string" ? j : "") || "Password change failed"; + return NextResponse.json({ error: reason }, { status: patchRes.status }); } return NextResponse.json({ ok: true });