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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user