Skip to content

Commit

Permalink
Add JWT verification for attachment route
Browse files Browse the repository at this point in the history
  • Loading branch information
SuperAuguste committed Aug 15, 2023
1 parent 4eb72d4 commit ea1ddc0
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 6 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This repository provides a Discord bot that syncs forum posts with Zendesk ticke
- [Zendesk Discord Integration](#zendesk-discord-integration)
- [Environment Variables](#environment-variables)
- [`SITE`](#site)
- [`JWT_SECRET`](#jwt_secret)
- [`PUSH` (optional but recommended)](#push-optional-but-recommended)
- [`QDRANT_URL` (optional)](#qdrant_url-optional)
- [`OPENAI_KEY` (optional)](#openai_key-optional)
Expand All @@ -28,6 +29,10 @@ You'll need to set the following environment variables, either in your command l

The site on which you'll be hosting the Zendesk channel server and Discord bot (e.g. `SITE=https://somesubdomain.sourcegraph.com`).

### `JWT_SECRET`

A passphrase used to sign JWTs.

### `PUSH` (optional but recommended)

The unique identifier of an OAuth client created under `Apps and integrations > APIs > Zendesk API > OAuth Clients`. Note that this is not a user authenticated interaction, so no redirect URL is required when creating this OAuth client.
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"ejs": "^3.1.9",
"express": "^4.18.2",
"form-data": "^4.0.0",
"jsonwebtoken": "^9.0.1",
"openai": "^3.3.0"
},
"devDependencies": {
Expand All @@ -24,6 +25,7 @@
"@sourcegraph/tsconfig": "^4.0.1",
"@types/body-parser": "^1.19.2",
"@types/express": "^4.17.17",
"@types/jsonwebtoken": "^9.0.2",
"@types/node-zendesk": "^2.0.11",
"jszip": "^3.10.1",
"ts-node": "^10.9.1",
Expand Down
50 changes: 47 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
MessageType,
} from 'discord.js'
import dotenv from 'dotenv'
import jwt from 'jsonwebtoken'
import { Configuration, OpenAIApi } from 'openai'

import { ChannelbackRequest, ClickthroughRequest, ExternalResource, Metadata } from './zendesk'
Expand Down Expand Up @@ -155,7 +156,10 @@ export async function createBot(params: BotParams): Promise<Bot> {
},
],
file_urls: starter.attachments.map(
attachment => `${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}`
attachment =>
`${process.env.SITE!}/attachment/${encodeURIComponent(
attachment.url
)}?token=${encodeURIComponent(jwt.sign(attachment.url, process.env.JWT_SECRET!))}`
),
})

Expand Down Expand Up @@ -271,7 +275,10 @@ export async function createBot(params: BotParams): Promise<Bot> {
internal_note: false,
allow_channelback: true,
file_urls: interaction.attachments.map(
attachment => `${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}`
attachment =>
`${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}?token=${encodeURIComponent(
jwt.sign(attachment.url, process.env.JWT_SECRET!)
)}`
),
})
})
Expand Down
11 changes: 10 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import axios from 'axios'
import bodyParser from 'body-parser'
import dotenv from 'dotenv'
import express from 'express'
import jwt from 'jsonwebtoken'

import { Bot, createBot } from './bot'
import { ChannelbackRequest, ExternalResource, Metadata } from './zendesk'

dotenv.config()

if (!process.env.SITE) {
if (!process.env.SITE || !process.env.JWT_SECRET) {
console.log('bad config')
process.exit(1)
}
Expand All @@ -27,7 +28,15 @@ app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))

app.all('/attachment/(*)', async (req, res) => {
if (typeof req.query.token !== 'string') {
res.status(403).send('Missing token')
return
}

try {
const token = jwt.verify(req.query.token, process.env.JWT_SECRET!)
if (token !== req.params[0]) throw new Error('Mismatch')

const { data } = await axios.get(req.params[0], {
responseType: 'stream',
timeout: 10_000,
Expand Down

0 comments on commit ea1ddc0

Please sign in to comment.