From 44edbf8a07be2e878d2e993a7fd5c226ae699251 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Wed, 10 Jan 2024 22:50:58 +0530 Subject: [PATCH 1/8] feat: adds new function to verify cron jobs auth token --- src/utils/verifyAuthToken.ts | 20 ++++++++++++ tests/unit/utils/verifyToken.test.ts | 48 +++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/utils/verifyAuthToken.ts b/src/utils/verifyAuthToken.ts index 52c64f01..a9200894 100644 --- a/src/utils/verifyAuthToken.ts +++ b/src/utils/verifyAuthToken.ts @@ -24,3 +24,23 @@ export async function verifyAuthToken(authHeader: string, env: env) { throw new Error(AUTHENTICATION_ERROR); } } + +/** + * + * @param authHeader { string } : the auth header of request + * @param env { env }: the ctx (context) which contains the secrets put in as wrangler secrets. + */ + +export async function verifyCronJobsToken(authHeader: string, env: env) { + const parts = authHeader.split(" "); + if (parts.length !== 2 || parts[0] !== "Bearer") { + throw new Error(INVALID_TOKEN_FORMAT); + } + const authToken = parts[1]; + const isValid = await jwt.verify(authToken, env.CRON_JOBS_PRIVATE_KEY, { + algorithm: "RS256", + }); + if (!isValid) { + throw new Error(AUTHENTICATION_ERROR); + } +} diff --git a/tests/unit/utils/verifyToken.test.ts b/tests/unit/utils/verifyToken.test.ts index 2c3c63f5..c411178f 100644 --- a/tests/unit/utils/verifyToken.test.ts +++ b/tests/unit/utils/verifyToken.test.ts @@ -1,5 +1,8 @@ import jwt from "@tsndr/cloudflare-worker-jwt"; -import { verifyAuthToken } from "../../../src/utils/verifyAuthToken"; +import { + verifyAuthToken, + verifyCronJobsToken, +} from "../../../src/utils/verifyAuthToken"; import { AUTHENTICATION_ERROR, INVALID_TOKEN_FORMAT, @@ -45,3 +48,46 @@ describe("verifyAuthToken", () => { ); }); }); + +describe("verifyCronJobsToken", () => { + const authToken = "validToken"; + const mockEnv = { CRON_JOBS_PRIVATE_KEY: "publicKey" }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it("should verify a valid token successfully", async () => { + jwt.verify = jest.fn().mockResolvedValue(true); + const authHeader = `Bearer ${authToken}`; + await expect( + verifyCronJobsToken(authHeader, mockEnv) + ).resolves.not.toThrow(); + expect(jwt.verify).toHaveBeenCalledWith( + authToken, + mockEnv.CRON_JOBS_PRIVATE_KEY, + { algorithm: "RS256" } + ); + }); + + it("should throw an error for an invalid token", async () => { + const authHeader = "Bearer invalidToken"; + jwt.verify = jest.fn().mockResolvedValue(false); + await expect(verifyCronJobsToken(authHeader, mockEnv)).rejects.toThrow( + AUTHENTICATION_ERROR + ); + }); + it("should throw an error when Bearer is not passed", async () => { + const authHeader = "Beaer invalidToken"; + await expect(verifyCronJobsToken(authHeader, mockEnv)).rejects.toThrow( + INVALID_TOKEN_FORMAT + ); + }); + + it("should throw an error for a malformed auth header", async () => { + const malformedHeader = "invalidformat"; + await expect(verifyCronJobsToken(malformedHeader, mockEnv)).rejects.toThrow( + INVALID_TOKEN_FORMAT + ); + }); +}); From 949b5b3b03f8062b6b6f132091cef0086bb45127 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Wed, 10 Jan 2024 22:52:38 +0530 Subject: [PATCH 2/8] chore: renames key names --- src/utils/verifyAuthToken.ts | 2 +- tests/unit/utils/verifyToken.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/verifyAuthToken.ts b/src/utils/verifyAuthToken.ts index a9200894..33a4328b 100644 --- a/src/utils/verifyAuthToken.ts +++ b/src/utils/verifyAuthToken.ts @@ -37,7 +37,7 @@ export async function verifyCronJobsToken(authHeader: string, env: env) { throw new Error(INVALID_TOKEN_FORMAT); } const authToken = parts[1]; - const isValid = await jwt.verify(authToken, env.CRON_JOBS_PRIVATE_KEY, { + const isValid = await jwt.verify(authToken, env.CRON_JOBS_PUBLIC_KEY, { algorithm: "RS256", }); if (!isValid) { diff --git a/tests/unit/utils/verifyToken.test.ts b/tests/unit/utils/verifyToken.test.ts index c411178f..c533b268 100644 --- a/tests/unit/utils/verifyToken.test.ts +++ b/tests/unit/utils/verifyToken.test.ts @@ -51,7 +51,7 @@ describe("verifyAuthToken", () => { describe("verifyCronJobsToken", () => { const authToken = "validToken"; - const mockEnv = { CRON_JOBS_PRIVATE_KEY: "publicKey" }; + const mockEnv = { CRON_JOBS_PUBLIC_KEY: "publicKey" }; afterEach(() => { jest.clearAllMocks(); @@ -65,7 +65,7 @@ describe("verifyCronJobsToken", () => { ).resolves.not.toThrow(); expect(jwt.verify).toHaveBeenCalledWith( authToken, - mockEnv.CRON_JOBS_PRIVATE_KEY, + mockEnv.CRON_JOBS_PUBLIC_KEY, { algorithm: "RS256" } ); }); From a1fe6ce8cde53dfa2251eca9dd573a29a5815164 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Wed, 10 Jan 2024 23:11:48 +0530 Subject: [PATCH 3/8] chore: changes function used to verify cron jobs token --- src/controllers/guildRoleHandler.ts | 4 ++-- tests/fixtures/fixture.ts | 1 + tests/unit/handlers/guildRoleHandler.test.ts | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/controllers/guildRoleHandler.ts b/src/controllers/guildRoleHandler.ts index 981b17f4..d3a60671 100644 --- a/src/controllers/guildRoleHandler.ts +++ b/src/controllers/guildRoleHandler.ts @@ -13,7 +13,7 @@ import { createNewRole, memberGroupRole, } from "../typeDefinitions/discordMessage.types"; -import { verifyAuthToken } from "../utils/verifyAuthToken"; +import { verifyAuthToken, verifyCronJobsToken } from "../utils/verifyAuthToken"; import { batchDiscordRequests } from "../utils/batchDiscordRequests"; import { DISCORD_BASE_URL } from "../constants/urls"; import { GROUP_ROLE_ADD } from "../constants/requestsActions"; @@ -56,7 +56,7 @@ export async function getGuildRolesPostHandler(request: IRequest, env: env) { } try { - await verifyAuthToken(authHeader, env); + await verifyCronJobsToken(authHeader, env); const { action } = request.query; switch (action) { diff --git a/tests/fixtures/fixture.ts b/tests/fixtures/fixture.ts index c56dceda..9d31bc8f 100644 --- a/tests/fixtures/fixture.ts +++ b/tests/fixtures/fixture.ts @@ -54,6 +54,7 @@ export const dummyAddRoleBody: memberGroupRole = { export const guildEnv = { DISCORD_GUILD_ID: "1234", DISCORD_TOKEN: "abcd", + CRON_JOBS_PUBLIC_KEY: "test", }; export const dummyInviteBody = { diff --git a/tests/unit/handlers/guildRoleHandler.test.ts b/tests/unit/handlers/guildRoleHandler.test.ts index cd7b89a5..581b693d 100644 --- a/tests/unit/handlers/guildRoleHandler.test.ts +++ b/tests/unit/handlers/guildRoleHandler.test.ts @@ -18,6 +18,7 @@ import { GROUP_ROLE_ADD } from "../../../src/constants/requestsActions"; jest.mock("../../../src/utils/verifyAuthToken", () => ({ verifyAuthToken: jest.fn().mockReturnValue(true), + verifyCronJobsToken: jest.fn().mockReturnValue(true), })); const getGuildRolesSpy = jest.spyOn(guildRoleUtils, "getGuildRoles"); From 6fd0180bfb470c89b2a3b59f269e549c2d0e436c Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Wed, 10 Jan 2024 23:40:38 +0530 Subject: [PATCH 4/8] chore: adds validations --- src/utils/verifyAuthToken.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/utils/verifyAuthToken.ts b/src/utils/verifyAuthToken.ts index 33a4328b..2356f800 100644 --- a/src/utils/verifyAuthToken.ts +++ b/src/utils/verifyAuthToken.ts @@ -32,11 +32,14 @@ export async function verifyAuthToken(authHeader: string, env: env) { */ export async function verifyCronJobsToken(authHeader: string, env: env) { - const parts = authHeader.split(" "); - if (parts.length !== 2 || parts[0] !== "Bearer") { + if (!authHeader) { throw new Error(INVALID_TOKEN_FORMAT); } - const authToken = parts[1]; + const authHeaderParts = authHeader.split(" "); + if (authHeaderParts.length !== 2 || authHeaderParts[0] !== "Bearer") { + throw new Error(INVALID_TOKEN_FORMAT); + } + const authToken = authHeaderParts[1]; const isValid = await jwt.verify(authToken, env.CRON_JOBS_PUBLIC_KEY, { algorithm: "RS256", }); From 37b37b3fec5c4ee98c75bd85106a21a21256e386 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Sat, 27 Jan 2024 06:50:24 +0530 Subject: [PATCH 5/8] feat: adds dev flag to auth --- src/controllers/guildRoleHandler.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/controllers/guildRoleHandler.ts b/src/controllers/guildRoleHandler.ts index d3a60671..e0da6b87 100644 --- a/src/controllers/guildRoleHandler.ts +++ b/src/controllers/guildRoleHandler.ts @@ -56,8 +56,13 @@ export async function getGuildRolesPostHandler(request: IRequest, env: env) { } try { - await verifyCronJobsToken(authHeader, env); - const { action } = request.query; + const { action, dev } = request.query; + //TODO(@Ajeyakrishna-k): remove dev flag https://github.com/Real-Dev-Squad/discord-slash-commands/issues/193 + if (dev) { + await verifyCronJobsToken(authHeader, env); + } else { + await verifyAuthToken(authHeader, env); + } switch (action) { case GROUP_ROLE_ADD.ADD_ROLE: { From 9457312376662fad4c6831c1a30d0d1afd012799 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Sat, 27 Jan 2024 07:15:07 +0530 Subject: [PATCH 6/8] chore: checks if dev flag is true --- src/controllers/guildRoleHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/guildRoleHandler.ts b/src/controllers/guildRoleHandler.ts index e0da6b87..c270d42c 100644 --- a/src/controllers/guildRoleHandler.ts +++ b/src/controllers/guildRoleHandler.ts @@ -58,7 +58,7 @@ export async function getGuildRolesPostHandler(request: IRequest, env: env) { try { const { action, dev } = request.query; //TODO(@Ajeyakrishna-k): remove dev flag https://github.com/Real-Dev-Squad/discord-slash-commands/issues/193 - if (dev) { + if (dev === 'true') { await verifyCronJobsToken(authHeader, env); } else { await verifyAuthToken(authHeader, env); From 726db40d20c58bed1bfcffd393e0ac11bdbefc08 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Sat, 27 Jan 2024 07:15:33 +0530 Subject: [PATCH 7/8] chore: runs prettier --- src/controllers/guildRoleHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/guildRoleHandler.ts b/src/controllers/guildRoleHandler.ts index c270d42c..bec5cd19 100644 --- a/src/controllers/guildRoleHandler.ts +++ b/src/controllers/guildRoleHandler.ts @@ -58,7 +58,7 @@ export async function getGuildRolesPostHandler(request: IRequest, env: env) { try { const { action, dev } = request.query; //TODO(@Ajeyakrishna-k): remove dev flag https://github.com/Real-Dev-Squad/discord-slash-commands/issues/193 - if (dev === 'true') { + if (dev === "true") { await verifyCronJobsToken(authHeader, env); } else { await verifyAuthToken(authHeader, env); From 419a9ddd0cddcc8b003cdc3962a6264ebfd53afd Mon Sep 17 00:00:00 2001 From: Ajeyakrishna <98796547+Ajeyakrishna-k@users.noreply.github.com> Date: Wed, 31 Jan 2024 23:25:42 +0530 Subject: [PATCH 8/8] chore: updates config (#196) --- .github/workflows/register-commands-production.yaml | 2 ++ .github/workflows/register-commands-staging.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/register-commands-production.yaml b/.github/workflows/register-commands-production.yaml index dd71f516..f0d161aa 100644 --- a/.github/workflows/register-commands-production.yaml +++ b/.github/workflows/register-commands-production.yaml @@ -35,6 +35,7 @@ jobs: CURRENT_ENVIRONMENT BOT_PRIVATE_KEY RDS_SERVERLESS_PUBLIC_KEY + CRON_JOBS_PUBLIC_KEY env: CURRENT_ENVIRONMENT: production CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_API_TOKEN}} @@ -43,3 +44,4 @@ jobs: BOT_PRIVATE_KEY: ${{secrets.BOT_PRIVATE_KEY}} DISCORD_GUILD_ID: ${{secrets.DISCORD_GUILD_ID}} RDS_SERVERLESS_PUBLIC_KEY: ${{secrets.RDS_SERVERLESS_PUBLIC_KEY}} + CRON_JOBS_PUBLIC_KEY: ${{secrets.CRON_JOBS_PUBLIC_KEY}} diff --git a/.github/workflows/register-commands-staging.yaml b/.github/workflows/register-commands-staging.yaml index c9c5df9d..d5d26209 100644 --- a/.github/workflows/register-commands-staging.yaml +++ b/.github/workflows/register-commands-staging.yaml @@ -34,6 +34,7 @@ jobs: DISCORD_GUILD_ID CURRENT_ENVIRONMENT BOT_PRIVATE_KEY + CRON_JOBS_PUBLIC_KEY env: CURRENT_ENVIRONMENT: staging CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_API_TOKEN}} @@ -41,3 +42,4 @@ jobs: DISCORD_TOKEN: ${{secrets.DISCORD_TOKEN}} BOT_PRIVATE_KEY: ${{secrets.BOT_PRIVATE_KEY}} DISCORD_GUILD_ID: ${{secrets.DISCORD_GUILD_ID}} + CRON_JOBS_PUBLIC_KEY: ${{secrets.CRON_JOBS_PUBLIC_KEY}}