aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2025-11-30 00:27:07 +0000
committerMohamed Bassem <me@mbassem.com>2025-11-30 00:27:20 +0000
commite4f434e730f4bb683523326f8e6fbeaffa0ab439 (patch)
tree5f92398faf33576d3f8f63993f5d54fd0a07eb38
parentd6d319d3a89466d0f0766d8ef81ba962b64143d6 (diff)
downloadkarakeep-e4f434e730f4bb683523326f8e6fbeaffa0ab439.tar.zst
fix: fix bypass email verification in apiKey.exchange
-rw-r--r--packages/trpc/routers/apiKeys.test.ts46
-rw-r--r--packages/trpc/routers/apiKeys.ts10
2 files changed, 56 insertions, 0 deletions
diff --git a/packages/trpc/routers/apiKeys.test.ts b/packages/trpc/routers/apiKeys.test.ts
index 1fd2159a..d68eaaf1 100644
--- a/packages/trpc/routers/apiKeys.test.ts
+++ b/packages/trpc/routers/apiKeys.test.ts
@@ -417,6 +417,52 @@ describe("API Keys Routes", () => {
}),
).rejects.toThrow(/UNAUTHORIZED/);
});
+
+ test<CustomTestContext>("rejects unverified user when email verification is enabled", async ({
+ db,
+ }) => {
+ // Import User model to create an unverified user directly
+ const { User } = await import("../models/users");
+
+ // Create user with password but without email verification
+ await User.createRaw(db, {
+ name: "Unverified User",
+ email: "unverified@test.com",
+ password: await (
+ await import("../auth")
+ ).hashPassword("password123", "test-salt"),
+ salt: "test-salt",
+ emailVerified: null, // User is not verified
+ });
+
+ // Mock serverConfig to enable email verification requirement
+ const originalConfig = (await import("@karakeep/shared/config")).default;
+ vi.spyOn(
+ originalConfig.auth,
+ "emailVerificationRequired",
+ "get",
+ ).mockReturnValue(true);
+
+ const { createCallerFactory } = await import("../index");
+ const { appRouter } = await import("./_app");
+ const createCaller = createCallerFactory(appRouter);
+ const caller = createCaller({
+ user: null,
+ db,
+ req: { ip: null },
+ });
+
+ // Attempting to exchange should fail with verification error
+ await expect(() =>
+ caller.apiKeys.exchange({
+ keyName: "Extension Key",
+ email: "unverified@test.com",
+ password: "password123",
+ }),
+ ).rejects.toThrow(/verify your email/i);
+
+ vi.restoreAllMocks();
+ });
});
describe("integration scenarios", () => {
diff --git a/packages/trpc/routers/apiKeys.ts b/packages/trpc/routers/apiKeys.ts
index 93b7d9ec..763bc23a 100644
--- a/packages/trpc/routers/apiKeys.ts
+++ b/packages/trpc/routers/apiKeys.ts
@@ -131,6 +131,16 @@ export const apiKeysAppRouter = router({
} catch {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
+
+ // Check if email verification is required and if the user has verified their email
+ if (serverConfig.auth.emailVerificationRequired && !user.emailVerified) {
+ throw new TRPCError({
+ message:
+ "Please verify your email address before generating an API key",
+ code: "FORBIDDEN",
+ });
+ }
+
return await generateApiKey(input.keyName, user.id, ctx.db);
}),
validate: publicProcedure