Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I can't verify requests for Discord interactions URL properly #1526

Open
preslavmihaylov opened this issue Apr 28, 2024 · 2 comments
Open

I can't verify requests for Discord interactions URL properly #1526

preslavmihaylov opened this issue Apr 28, 2024 · 2 comments

Comments

@preslavmihaylov
Copy link

preslavmihaylov commented Apr 28, 2024

I'm trying to write a golang handler, which reacts to discord interactions. I've set it up as the interactions URL in my discord developer portal.

This is the code I've written:

var secrets struct {
	DiscordPublicKey string
}

type UnknownDiscordInteraction struct {
	InteractionType discordgo.InteractionType `json:"type"`
}

func DiscordWebhook(w http.ResponseWriter, r *http.Request) {
	body, err := io.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Error reading request body", http.StatusInternalServerError)
		return
	}

	publicKeyBytes, err := hex.DecodeString(secrets.DiscordPublicKey)
	if err != nil {
		http.Error(w, "Error decoding hex string", http.StatusInternalServerError)
		return
	}

	if !discordgo.VerifyInteraction(r, publicKeyBytes) {
		http.Error(w, "invalid request signature", http.StatusUnauthorized)
		return
	}

	var interaction UnknownDiscordInteraction
	if err := json.Unmarshal(body, &interaction); err != nil {
		http.Error(w, "Error unmarshalling request body", http.StatusInternalServerError)
		return
	}

	if interaction.InteractionType == discordgo.InteractionPing {
		pongResp, err := json.Marshal(discordgo.InteractionResponse{
			Type: discordgo.InteractionResponsePong,
		})
		if err != nil {
			http.Error(w, "Error marshalling response", http.StatusInternalServerError)
			return
		}

		// set content-type header
		w.Header().Set("Content-Type", "application/json")
		w.Write(pongResp)
		w.WriteHeader(http.StatusOK)
		return
	}

	w.WriteHeader(http.StatusOK)
}

I can't save the endpoint in the developer dashboard, because any challenge request that discord sends I fail to verify, it returns that the request is invalid.

I am passing the DiscordPublicKey as a string, copied from my discord developer dashboard. Anyone has a clue what I'm doing wrong?

@piesuke
Copy link

piesuke commented Jul 24, 2024

@preslavmihaylov
I'm facing the same problem.

Did you find a solution?

@preslavmihaylov
Copy link
Author

preslavmihaylov commented Jul 26, 2024

No, I ended up writing a proxy in javascript which forwards discord requests to my go service to workaround this.

This is the JS code to handle that in case it's useful (deployed in Render):

import Fastify from "fastify";
import { Events } from "discord.js";

import { Client, GatewayIntentBits } from "discord.js";

const PROXY_ENDPOINT = process.env.PROXY_ENDPOINT || "";
const DISCORD_TOKEN = process.env.DISCORD_TOKEN || "";
const DISCORD_HANDLER_SECRET_TOKEN =
  process.env.DISCORD_HANDLER_SECRET_TOKEN || "";
// const DISCORD_APP_ID = process.env.DISCORD_APP_ID || "";
// const DISCORD_PUBLIC_KEY = process.env.DISCORD_PUBLIC_KEY || "";

const discordClient = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMembers,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

discordClient.once(Events.ClientReady, (readyClient) => {
  console.log(`Discord bot ready! Logged in as ${readyClient.user.tag}`);
});

discordClient.on(Events.MessageCreate, async (message) => {
  console.log(
    `Received message in ${message.channelId} from user ${message.author.id}`
  );
  console.log(message.toJSON());
  const response = await fetch(PROXY_ENDPOINT, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${DISCORD_HANDLER_SECRET_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(message.toJSON()),
  });
  const json = await response.json();
  console.log("Received response from proxy:", json);
});

discordClient.on(Events.InteractionCreate, async (interaction) => {
  if (interaction.isChatInputCommand()) {
    console.log("Received chat input command");
  }
  if (interaction.isMessageContextMenuCommand()) {
    console.log("Received message context menu command");
  }
  if (interaction.isModalSubmit()) {
    console.log("Received modal submission");
  }
});

discordClient.login(DISCORD_TOKEN);

const host = "RENDER" in process.env ? `0.0.0.0` : `localhost`;
const port = process.env.PORT || "4000";

const fastify = Fastify({
  logger: true,
});

// render healthcheck
fastify.head("/", async (_request, reply) => {
  reply.send();
});

fastify.get("/", async (_request, reply) => {
  reply.send();
});

fastify.listen({ host, port: parseInt(port) }, (err, address) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }

  console.log(`Server is now listening on ${address}`);
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants