SPB Git
1.6 KB · 25 lines typescript
1import { z } from "zod";2import { eq } from "drizzle-orm";3import { db, schema } from "@/db";4import { hashPassword, verifyPassword, passwordPolicy } from "@/lib/auth/password";5import { getSession, revokeOtherSessions, rotateSession, setSessionCookie } from "@/lib/auth/session";6import { assertSameOrigin, jsonError, HttpError } from "@/lib/auth/guard";7import { verifyCsrf } from "@/lib/auth/session";8import { audit } from "@/lib/audit";9export async function POST(req: Request) {10  try {11    await assertSameOrigin();12    const s = await getSession(); if (!s) throw new HttpError(401, "Unauthenticated");13    if (s.totpEnabled && !s.mfaPassed) throw new HttpError(403, "MFA required");14    if (!verifyCsrf(s, req.headers.get("x-csrf-token"))) throw new HttpError(403, "CSRF token invalid");15    const { current, next } = z.object({ current: z.string().max(500), next: z.string().max(500) }).parse(await req.json());16    const policy = passwordPolicy(next); if (policy) throw new HttpError(400, policy);17    const [u] = await db.select().from(schema.users).where(eq(schema.users.id, s.id));18    if (!(await verifyPassword(u.passwordHash, current))) throw new HttpError(401, "Current password is incorrect");19    await db.update(schema.users).set({ passwordHash: await hashPassword(next), mustChangePassword: false, updatedAt: new Date() }).where(eq(schema.users.id, s.id));20    await revokeOtherSessions(s.id, s.sessionId); const t = await rotateSession(s.sessionId); await setSessionCookie(t);21    await audit({ id: s.id, name: s.email }, "auth.password_changed");22    return Response.json({ ok: true });23  } catch (e) { return jsonError(e); }24}25
Files · main
Menu

Theme