spb/note-spboucher-ai
Public
1import { z } from "zod";2import { eq } from "drizzle-orm";3import { handler, parseBody, HttpError } from "@/lib/api";4import { db, schema } from "@/db";5import { verifyPassword, hashPassword, passwordPolicy } from "@/lib/auth/password";6import { revokeAllSessions, clientIp } from "@/lib/auth/session";7import { rateLimit } from "@/lib/auth/ratelimit";8import { audit } from "@/lib/audit";9import { headers } from "next/headers";1011/** POST { current, next } → change password (current password check), then revoke every other session. */12export const POST = handler(async ({ req, user }) => {13 const ip = clientIp(await headers());14 if (!rateLimit(`pw:${user.id}`, 5, 15 * 60_000)) throw new HttpError(429, "Too many attempts, try again later");15 const b = await parseBody(req, z.object({ current: z.string().min(1), next: z.string().min(1).max(200) }));16 const policy = passwordPolicy(b.next); if (policy) throw new HttpError(400, policy);17 if (b.next === b.current) throw new HttpError(400, "New password must differ from the current one.");18 const [u] = await db.select({ passwordHash: schema.users.passwordHash }).from(schema.users).where(eq(schema.users.id, user.id));19 if (!u || !(await verifyPassword(u.passwordHash, b.current))) { await audit(user.id, "auth.password_change_failed", undefined, {}, ip); throw new HttpError(400, "Current password is incorrect."); }20 await db.update(schema.users).set({ passwordHash: await hashPassword(b.next), updatedAt: new Date() }).where(eq(schema.users.id, user.id));21 await revokeAllSessions(user.id, user.sessionId);22 await audit(user.id, "auth.password_changed", undefined, { revokedOthers: true }, ip);23 return Response.json({ ok: true });24});25