aboutsummaryrefslogtreecommitdiffstats
path: root/packages/trpc/auth.ts
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--packages/trpc/auth.ts55
1 files changed, 39 insertions, 16 deletions
diff --git a/packages/trpc/auth.ts b/packages/trpc/auth.ts
index a01288d8..01966b9e 100644
--- a/packages/trpc/auth.ts
+++ b/packages/trpc/auth.ts
@@ -1,28 +1,33 @@
-import { randomBytes } from "crypto";
+import { createHash, randomBytes } from "crypto";
import * as bcrypt from "bcryptjs";
-import { db } from "@karakeep/db";
import { apiKeys } from "@karakeep/db/schema";
import serverConfig from "@karakeep/shared/config";
-// API Keys
+import type { Context } from "./index";
const BCRYPT_SALT_ROUNDS = 10;
-const API_KEY_PREFIX = "ak1";
+const API_KEY_PREFIX_V1 = "ak1";
+const API_KEY_PREFIX_V2 = "ak2";
export function generatePasswordSalt() {
return randomBytes(32).toString("hex");
}
-export async function generateApiKey(name: string, userId: string) {
+export async function generateApiKey(
+ name: string,
+ userId: string,
+ database: Context["db"],
+) {
const id = randomBytes(10).toString("hex");
- const secret = randomBytes(10).toString("hex");
- const secretHash = await bcrypt.hash(secret, BCRYPT_SALT_ROUNDS);
+ const secret = randomBytes(16).toString("hex");
- const plain = `${API_KEY_PREFIX}_${id}_${secret}`;
+ const secretHash = createHash("sha256").update(secret).digest("base64");
+
+ const plain = `${API_KEY_PREFIX_V2}_${id}_${secret}`;
const key = (
- await db
+ await database
.insert(apiKeys)
.values({
name: name,
@@ -40,6 +45,7 @@ export async function generateApiKey(name: string, userId: string) {
key: plain,
};
}
+
function parseApiKey(plain: string) {
const parts = plain.split("_");
if (parts.length != 3) {
@@ -47,18 +53,19 @@ function parseApiKey(plain: string) {
`Malformd API key. API keys should have 3 segments, found ${parts.length} instead.`,
);
}
- if (parts[0] !== API_KEY_PREFIX) {
+ if (parts[0] !== API_KEY_PREFIX_V1 && parts[0] !== API_KEY_PREFIX_V2) {
throw new Error(`Malformd API key. Got unexpected key prefix.`);
}
return {
+ version: parts[0] == API_KEY_PREFIX_V1 ? (1 as const) : (2 as const),
keyId: parts[1],
keySecret: parts[2],
};
}
-export async function authenticateApiKey(key: string) {
- const { keyId, keySecret } = parseApiKey(key);
- const apiKey = await db.query.apiKeys.findFirst({
+export async function authenticateApiKey(key: string, database: Context["db"]) {
+ const { version, keyId, keySecret } = parseApiKey(key);
+ const apiKey = await database.query.apiKeys.findFirst({
where: (k, { eq }) => eq(k.keyId, keyId),
with: {
user: true,
@@ -71,7 +78,19 @@ export async function authenticateApiKey(key: string) {
const hash = apiKey.keyHash;
- const validation = await bcrypt.compare(keySecret, hash);
+ let validation = false;
+ switch (version) {
+ case 1:
+ validation = await bcrypt.compare(keySecret, hash);
+ break;
+ case 2:
+ validation =
+ createHash("sha256").update(keySecret).digest("base64") == hash;
+ break;
+ default:
+ throw new Error("Invalid API Key");
+ }
+
if (!validation) {
throw new Error("Invalid API Key");
}
@@ -83,11 +102,15 @@ 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) {
+export async function validatePassword(
+ email: string,
+ password: string,
+ database: Context["db"],
+) {
if (serverConfig.auth.disablePasswordAuth) {
throw new Error("Password authentication is currently disabled");
}
- const user = await db.query.users.findFirst({
+ const user = await database.query.users.findFirst({
where: (u, { eq }) => eq(u.email, email),
});