aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-04-15 19:36:51 +0000
committerMohamed Bassem <me@mbassem.com>2025-04-15 19:36:51 +0000
commit7e39afa29f1674df4cac51c7894181f55f66aa12 (patch)
tree55caff2f4d14e222a2d9c2b63157d28a438a96e7 /packages/trpc
parentd7244978e9e99ca20b99a9f751b1bfef77810e94 (diff)
downloadkarakeep-7e39afa29f1674df4cac51c7894181f55f66aa12.tar.zst
fix: Add password salt to the user table
Diffstat (limited to 'packages/trpc')
-rw-r--r--packages/trpc/auth.ts13
-rw-r--r--packages/trpc/routers/admin.ts7
-rw-r--r--packages/trpc/routers/users.ts10
3 files changed, 21 insertions, 9 deletions
diff --git a/packages/trpc/auth.ts b/packages/trpc/auth.ts
index f5ce88e5..1c3b860d 100644
--- a/packages/trpc/auth.ts
+++ b/packages/trpc/auth.ts
@@ -11,6 +11,10 @@ import { authFailureLogger } from "@karakeep/shared/logger";
const BCRYPT_SALT_ROUNDS = 10;
const API_KEY_PREFIX = "ak1";
+export function generatePasswordSalt() {
+ return randomBytes(32).toString("hex");
+}
+
export async function generateApiKey(name: string, userId: string) {
const id = randomBytes(10).toString("hex");
const secret = randomBytes(10).toString("hex");
@@ -76,8 +80,8 @@ export async function authenticateApiKey(key: string) {
return apiKey.user;
}
-export async function hashPassword(password: string) {
- return bcrypt.hash(password, BCRYPT_SALT_ROUNDS);
+export async function hashPassword(password: string, salt: string | null) {
+ return await bcrypt.hash(password + (salt ?? ""), BCRYPT_SALT_ROUNDS);
}
export async function validatePassword(email: string, password: string) {
@@ -96,7 +100,10 @@ export async function validatePassword(email: string, password: string) {
throw new Error("This user doesn't have a password defined");
}
- const validation = await bcrypt.compare(password, user.password);
+ const validation = await bcrypt.compare(
+ password + (user.salt ?? ""),
+ user.password,
+ );
if (!validation) {
throw new Error("Wrong password");
}
diff --git a/packages/trpc/routers/admin.ts b/packages/trpc/routers/admin.ts
index 9b44f7c9..85869ba8 100644
--- a/packages/trpc/routers/admin.ts
+++ b/packages/trpc/routers/admin.ts
@@ -22,7 +22,7 @@ import {
zAdminCreateUserSchema,
} from "@karakeep/shared/types/admin";
-import { hashPassword } from "../auth";
+import { generatePasswordSalt, hashPassword } from "../auth";
import { adminProcedure, router } from "../index";
import { createUser } from "./users";
@@ -338,10 +338,11 @@ export const adminAppRouter = router({
message: "Cannot reset own password",
});
}
- const hashedPassword = await hashPassword(input.newPassword);
+ const newSalt = generatePasswordSalt();
+ const hashedPassword = await hashPassword(input.newPassword, newSalt);
const result = await ctx.db
.update(users)
- .set({ password: hashedPassword })
+ .set({ password: hashedPassword, salt: newSalt })
.where(eq(users.id, input.userId));
if (result.changes == 0) {
diff --git a/packages/trpc/routers/users.ts b/packages/trpc/routers/users.ts
index 75a1db0c..c56daaee 100644
--- a/packages/trpc/routers/users.ts
+++ b/packages/trpc/routers/users.ts
@@ -19,7 +19,7 @@ import {
zWhoAmIResponseSchema,
} from "@karakeep/shared/types/users";
-import { hashPassword, validatePassword } from "../auth";
+import { generatePasswordSalt, hashPassword, validatePassword } from "../auth";
import {
adminProcedure,
authedProcedure,
@@ -42,13 +42,15 @@ export async function createUser(
userRole = userCount == 0 ? "admin" : "user";
}
+ const salt = generatePasswordSalt();
try {
const result = await trx
.insert(users)
.values({
name: input.name,
email: input.email,
- password: await hashPassword(input.password),
+ password: await hashPassword(input.password, salt),
+ salt,
role: userRole,
})
.returning({
@@ -149,10 +151,12 @@ export const usersAppRouter = router({
throw new TRPCError({ code: "UNAUTHORIZED" });
}
invariant(user.id, ctx.user.id);
+ const newSalt = generatePasswordSalt();
await ctx.db
.update(users)
.set({
- password: await hashPassword(input.newPassword),
+ password: await hashPassword(input.newPassword, newSalt),
+ salt: newSalt,
})
.where(eq(users.id, ctx.user.id));
}),