/** * Set password procedure - sets or changes user password */ import { ORPCError } from "@orpc/server"; import { hashPassword, validatePassword, verifyPassword, } from "../../utils/password.js"; import { meRoute } from "./_base.js"; /** * Set password handler * - Requires authentication * - If user has existing password, currentPassword is required * - Validates new password strength using zxcvbn */ export const setPassword = meRoute.setPassword.handler( async ({ input, context }) => { const { currentPassword, newPassword } = input; // Fetch current password hash const user = await context.db .selectFrom("users") .select(["password_hash"]) .where("id", "=", context.user.id) .executeTakeFirstOrThrow(); // If user has a password, verify current password if (user.password_hash) { if (!currentPassword) { throw new ORPCError("BAD_REQUEST", { message: "Current password required", }); } const valid = await verifyPassword(currentPassword, user.password_hash); if (!valid) { throw new ORPCError("BAD_REQUEST", { message: "Current password is incorrect", }); } } // Validate new password strength const validation = validatePassword(newPassword, [context.user.email]); if (!validation.valid) { throw new ORPCError("BAD_REQUEST", { message: validation.feedback[0] ?? "Password is too weak", }); } // Hash and update const newHash = await hashPassword(newPassword); await context.db .updateTable("users") .set({ password_hash: newHash, updated_at: new Date() }) .where("id", "=", context.user.id) .execute(); return { success: true }; }, );