Refactor me.* procedures with code review fixes

- Fix silent failures: add 404 NOT_FOUND for invalid resources in
  passkeysRename, revokeSession, trustDevice, untrustDevice
- Fix race condition in passkeysDelete using transaction
- Extract helper functions: requireDeviceFingerprint, defaultDeviceName
- Improve type safety in updateProfile with Kysely's Updateable<Users>
- Extract me.* procedures to separate files under procedures/me/
- Standardize naming to verb-first: listPasskeys, renamePasskey, deletePasskey

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 16:24:10 +08:00
parent 1ebcf12cb9
commit 9b898678c7
10 changed files with 555 additions and 125 deletions

View File

@@ -0,0 +1,61 @@
/**
* Set password procedure - sets or changes user password
*/
import { ORPCError } from "@orpc/server";
import {
hashPassword,
validatePassword,
verifyPassword,
} from "../../utils/password.js";
import { authMiddleware, os } 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 = os.me.setPassword
.use(authMiddleware)
.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();
});