Authorize Discord Bot With Cloudflare Workers

Verify Discord Interactions

It took me a while to get my Discord Bot running on Cloudflare Workers. I didn’t want to pull in all the existing frameworks for just one simple, single command I want on my Discord server.

I went out on a quest and tried to configure a Discord Bot on my own with the available Documentation. It was super, duper, I mean, really, super hard to get the right information from the Discord Developer Documentation. I don’t know how such a rich ecosystem of Bots can exist with so many missing pieces in the docs. It might also just be me. But hey, I’ve got it working eventually, and if anyone comes across the same issues: Read on.

Anyway, I’ve set up a Cloudflare Worker to respond to slash commands or application commands in Discord to call an external API. To make this happen, you need to create a Bot in the Discord Developers Portal and configure a custom INTERACTIONS ENDPOINT URL

INTERACTIONS ENDPOINT URL in the Discord Developer Portal

Under Security and Authorization they describe what’s required to verify a custom endpoint URL but are missing an important bit I’ll explain in a bit.

In short, they want to verify your endpoint by sending a signature, timestamp and the body to your endpoint, it needs to be verified and return with status code 200 or status code 401 if it fails. For verification, they call two requests and test if one responds successfully and one as failed. In their code examples, they just mention the status codes, but they also want to get the payload they send returned by you. This wasn't obvious to me in the docs, and also not in the error message I got on the Discord Developers page:

Validation errors: interactions_endpoint_url: The specified interactions endpoint URL could not be verified.

Only after checking in the Developer JavaScript Console, I saw that the body was malformed or missing. D’oh!

Also, with Cloudflare Workers you don’t have access to Buffer from NodeJS which they use in their examples, so we use another buffer package which works in Browsers and Cloudflare Workers. Let's get to it.

Verify and Authorize Discord Interactions

First, install these two packages: buffer and tweetnacl:

npm i tweetnacl buffer

Now, in your Workers JavaScript, import the two:

import nacl from 'tweetnacl'
const Buffer = require('buffer/').Buffer

Here’s the handleRequest function of a Cloudflare Worker, please update the PUBLIC_KEY variable:

async function handleRequest(request, env) {
if (request.method === 'POST') {
const req = await request.json()

const headers = request.headers
const PUBLIC_KEY = 'your-public_key-here'
const signature = headers.get('X-Signature-Ed25519')
const timestamp = headers.get('X-Signature-Timestamp')

if (signature && timestamp) {
const isVerified = nacl.sign.detached.verify(
Buffer(timestamp + JSON.stringify(req)),
Buffer(signature, 'hex'),
Buffer(PUBLIC_KEY, 'hex'),
)

if (!isVerified) {
return new Response(JSON.stringify(req), { status: 401 })
} else {
return new Response(JSON.stringify(req), { status: 200 })
}
}
}
}

Now, if you’re going to validate your Discord Application, it should succeed since the response will send the correct status code and also the body back as a string to Discord.

Fixing this whole thing took longer than I want to admit. Perhaps I can finally start building the actual bot next week and post an update.