SPB Git
1.7 KB · 25 lines typescript
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
Files · main
Menu

Theme