From 59dbc8ed068e13ab4786b53be769f26f91f6e1e2 Mon Sep 17 00:00:00 2001 From: ayntee Date: Mon, 19 Apr 2021 11:51:05 +0400 Subject: [PATCH] Use nacl for signature verification and remove server.ts (included in template) --- src/interactions/README.md | 9 --- src/interactions/deps.ts | 3 +- src/interactions/mod.ts | 2 +- src/interactions/server.ts | 130 ------------------------------------- 4 files changed, 2 insertions(+), 142 deletions(-) delete mode 100644 src/interactions/README.md delete mode 100644 src/interactions/server.ts diff --git a/src/interactions/README.md b/src/interactions/README.md deleted file mode 100644 index 5f8cb5b36..000000000 --- a/src/interactions/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Discordeno Interactions - -`interactions` is a standalone submodule that supports the webhooks through HTTP -server to add support for Discord's interactions feature. This is a barebones -interface that will create and handle requests from Discord API. - -- Complete and extremely fast security and verification checks -- First-class TypeScript Support -- Minimalistic diff --git a/src/interactions/deps.ts b/src/interactions/deps.ts index e4d49c4c8..9c4f4dbfd 100644 --- a/src/interactions/deps.ts +++ b/src/interactions/deps.ts @@ -1,2 +1 @@ -export { serve } from "https://deno.land/std@0.90.0/http/server.ts"; -export { verify } from "https://unpkg.com/@evan/wasm@0.0.50/target/ed25519/deno.js"; +export { sign } from "https://unpkg.com/@evan/wasm@0.0.50/target/nacl/deno.js"; diff --git a/src/interactions/mod.ts b/src/interactions/mod.ts index 868f42d4c..0d89dc128 100644 --- a/src/interactions/mod.ts +++ b/src/interactions/mod.ts @@ -1 +1 @@ -export * from "./server.ts"; +export * from "./verify_signature.ts"; diff --git a/src/interactions/server.ts b/src/interactions/server.ts deleted file mode 100644 index be52423b8..000000000 --- a/src/interactions/server.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { - DiscordInteractionResponseTypes, - DiscordInteractionTypes, -} from "../types/mod.ts"; -import { serve, verify } from "./deps.ts"; - -/** This variable is a holder for the public key and other configuration */ -const serverOptions = { - publicKey: "", - port: 80, -}; - -/** Theses are the handlers that you can plug into and customize to your needs. */ -export const handlers = { - handlePayload, - handleApplicationCommand, -}; - -/** Starts the slash command server */ -export async function startServer( - { port, publicKey, handleApplicationCommand }: StartServerConfig, -) { - serverOptions.publicKey = publicKey; - serverOptions.port = port; - if (handleApplicationCommand) { - handlers.handleApplicationCommand = handleApplicationCommand; - } - - const server = serve({ port: serverOptions.port }); - - for await (const req of server) { - // TODO: this is going to be removed - // deno-lint-ignore no-deprecated-deno-api - const buffer = await Deno.readAll(req.body); - const signature = req.headers.get("X-Signature-Ed25519"); - const timestamp = req.headers.get("X-Signature-Timestamp"); - - if (!signature || !timestamp) { - req.respond({ status: 400, body: "Bad request" }); - continue; - } - - const isVerified = verifySecurity(buffer, signature!, timestamp!); - if (!isVerified) { - req.respond({ status: 401, body: "Invalid request signature" }); - continue; - } - - try { - const data = JSON.parse(new TextDecoder().decode(buffer)); - const response = await handlers.handlePayload(data); - req.respond( - { status: response.status || 200, body: JSON.stringify(response.body) }, - ); - } catch (error) { - console.error(error); - } - } -} - -function handlePayload(payload: Interaction) { - switch (payload.type) { - case DiscordInteractionTypes.Ping: - return { - status: 200, - body: { type: DiscordInteractionResponseTypes.Pong }, - }; - default: // APPLICATION_COMMAND - return handlers.handleApplicationCommand(payload); - } -} - -/** The function that handles your commands. This command can be overriden by you and you can receive the payload and handle accordingly and respond back. The status if not provided will default to 200. */ -// deno-lint-ignore require-await -async function handleApplicationCommand( - payload: Interaction, -): Promise<{ status?: number; body: InteractionResponse }> { - // Handle the command - if (payload.data?.name === "ping") { - return { - status: 200, - body: { - type: DiscordInteractionResponseTypes.ChannelMessageWithSource, - data: { content: "Pong from Discordeno!" }, - }, - }; - } - - return { - status: 200, - body: { - type: DiscordInteractionResponseTypes.ChannelMessageWithSource, - data: { - content: - "Whoopsies! Seems the handling for this command is missing. Please contact my developers!", - }, - }, - }; -} - -/** Internal function to verify security. Discord will send bad and good data and this function is important to verify it. If it is not verified properly, Discord will kill your bot. */ -export function verifySecurity( - buffer: Uint8Array, - signature: string, - time: string, -) { - const sig = new Uint8Array(64); - const timestamp = new TextEncoder().encode(time); - - let offset = 0; - const message = new Uint8Array(buffer.length + timestamp.length); - while (offset < 2 * 64) { - sig[offset / 2] = parseInt(signature!.substring(offset, offset += 2), 16); - } - - const slashKey = new Uint8Array(32); - let keyoffset = 0; - - while (keyoffset < 2 * 32) { - slashKey[keyoffset / 2] = parseInt( - serverOptions.publicKey.substring(keyoffset, keyoffset += 2), - 16, - ); - } - - message.set(timestamp); - message.set(buffer, timestamp.length); - - return verify(slashKey, sig, message); -}