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

@@ -14,11 +14,30 @@ import {
loginRequestMiddleware,
os,
} from "./procedures/base.js";
import { meDelete } from "./procedures/me/delete.js";
import {
getDeviceInfo,
listTrustedDevices,
revokeAllTrustedDevices,
trustDevice,
untrustDevice,
} from "./procedures/me/devices.js";
import {
deletePasskey,
listPasskeys,
renamePasskey,
} from "./procedures/me/passkeys.js";
import {
listSessions,
revokeAllSessions,
revokeSession,
} from "./procedures/me/sessions.js";
import { setPassword } from "./procedures/me/set-password.js";
import { updateProfile } from "./procedures/me/update-profile.js";
import {
createAuthenticationOptions as createAuthOptions,
createRegistrationOptions as createRegOptions,
getRPInfo,
getUserPasskeys,
verifyAuthentication as verifyAuth,
verifyRegistration as verifyReg,
} from "./utils/webauthn.js";
@@ -115,122 +134,11 @@ const setupProfile = os.me.setupProfile
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const updateProfile = os.me.updateProfile
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const meDelete = os.me.delete.use(authMiddleware).handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const setPassword = os.me.setPassword.use(authMiddleware).handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const passkeysList = os.me.passkeys.list
.use(authMiddleware)
.handler(async ({ context }) => {
const passkeys = await getUserPasskeys(context.db, context.user.id);
return passkeys.map((p) => ({
id: p.id,
name: p.name,
createdAt: p.createdAt,
lastUsedAt: p.lastUsedAt,
}));
});
const passkeysRename = os.me.passkeys.rename
.use(authMiddleware)
.handler(async ({ input, context }) => {
const { passkeyId, name } = input;
await context.db
.updateTable("passkeys")
.set({ name })
.where("id", "=", String(passkeyId))
.where("user_id", "=", context.user.id)
.execute();
});
const passkeysDelete = os.me.passkeys.delete
.use(authMiddleware)
.handler(async ({ input, context }) => {
const { passkeyId } = input;
// Check if this is the last passkey and user has no password
const user = await context.db
.selectFrom("users")
.select(["password_hash"])
.where("id", "=", context.user.id)
.executeTakeFirst();
const passkeyCount = await context.db
.selectFrom("passkeys")
.select(context.db.fn.countAll().as("count"))
.where("user_id", "=", context.user.id)
.executeTakeFirst();
if (!user?.password_hash && Number(passkeyCount?.count ?? 0) <= 1) {
throw new ORPCError("BAD_REQUEST", {
message: "Cannot delete the last passkey when you have no password set",
});
}
await context.db
.deleteFrom("passkeys")
.where("id", "=", String(passkeyId))
.where("user_id", "=", context.user.id)
.execute();
});
const listSessions = os.me.listSessions
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const revokeSession = os.me.revokeSession
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const revokeAllSessions = os.me.revokeAllSessions
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const getDeviceInfo = os.me.getDeviceInfo
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const trustDevice = os.me.trustDevice.use(authMiddleware).handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const listTrustedDevices = os.me.listTrustedDevices
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const untrustDevice = os.me.untrustDevice
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
const revokeAllTrustedDevices = os.me.revokeAllTrustedDevices
.use(authMiddleware)
.handler(async () => {
throw new ORPCError("NOT_IMPLEMENTED", { message: "Not implemented" });
});
// Me procedures imported from ./procedures/me/*
// - updateProfile, setPassword, meDelete
// - listPasskeys, renamePasskey, deletePasskey
// - listSessions, revokeSession, revokeAllSessions
// - getDeviceInfo, trustDevice, listTrustedDevices, untrustDevice, revokeAllTrustedDevices
// Orgs procedures (all require auth)
const orgsList = os.orgs.list.use(authMiddleware).handler(async () => {
@@ -418,9 +326,9 @@ export const router = os.router({
delete: meDelete,
setPassword,
passkeys: {
list: passkeysList,
rename: passkeysRename,
delete: passkeysDelete,
list: listPasskeys,
rename: renamePasskey,
delete: deletePasskey,
},
listSessions,
revokeSession,