Skip to content

Commit ea1ddc0

Browse files
committed
Add JWT verification for attachment route
1 parent 4eb72d4 commit ea1ddc0

File tree

5 files changed

+73
-6
lines changed

5 files changed

+73
-6
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ This repository provides a Discord bot that syncs forum posts with Zendesk ticke
88
- [Zendesk Discord Integration](#zendesk-discord-integration)
99
- [Environment Variables](#environment-variables)
1010
- [`SITE`](#site)
11+
- [`JWT_SECRET`](#jwt_secret)
1112
- [`PUSH` (optional but recommended)](#push-optional-but-recommended)
1213
- [`QDRANT_URL` (optional)](#qdrant_url-optional)
1314
- [`OPENAI_KEY` (optional)](#openai_key-optional)
@@ -28,6 +29,10 @@ You'll need to set the following environment variables, either in your command l
2829

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

32+
### `JWT_SECRET`
33+
34+
A passphrase used to sign JWTs.
35+
3136
### `PUSH` (optional but recommended)
3237

3338
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.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"ejs": "^3.1.9",
1717
"express": "^4.18.2",
1818
"form-data": "^4.0.0",
19+
"jsonwebtoken": "^9.0.1",
1920
"openai": "^3.3.0"
2021
},
2122
"devDependencies": {
@@ -24,6 +25,7 @@
2425
"@sourcegraph/tsconfig": "^4.0.1",
2526
"@types/body-parser": "^1.19.2",
2627
"@types/express": "^4.17.17",
28+
"@types/jsonwebtoken": "^9.0.2",
2729
"@types/node-zendesk": "^2.0.11",
2830
"jszip": "^3.10.1",
2931
"ts-node": "^10.9.1",

pnpm-lock.yaml

Lines changed: 47 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bot.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
MessageType,
1616
} from 'discord.js'
1717
import dotenv from 'dotenv'
18+
import jwt from 'jsonwebtoken'
1819
import { Configuration, OpenAIApi } from 'openai'
1920

2021
import { ChannelbackRequest, ClickthroughRequest, ExternalResource, Metadata } from './zendesk'
@@ -155,7 +156,10 @@ export async function createBot(params: BotParams): Promise<Bot> {
155156
},
156157
],
157158
file_urls: starter.attachments.map(
158-
attachment => `${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}`
159+
attachment =>
160+
`${process.env.SITE!}/attachment/${encodeURIComponent(
161+
attachment.url
162+
)}?token=${encodeURIComponent(jwt.sign(attachment.url, process.env.JWT_SECRET!))}`
159163
),
160164
})
161165

@@ -271,7 +275,10 @@ export async function createBot(params: BotParams): Promise<Bot> {
271275
internal_note: false,
272276
allow_channelback: true,
273277
file_urls: interaction.attachments.map(
274-
attachment => `${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}`
278+
attachment =>
279+
`${process.env.SITE!}/attachment/${encodeURIComponent(attachment.url)}?token=${encodeURIComponent(
280+
jwt.sign(attachment.url, process.env.JWT_SECRET!)
281+
)}`
275282
),
276283
})
277284
})

src/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import axios from 'axios'
22
import bodyParser from 'body-parser'
33
import dotenv from 'dotenv'
44
import express from 'express'
5+
import jwt from 'jsonwebtoken'
56

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

910
dotenv.config()
1011

11-
if (!process.env.SITE) {
12+
if (!process.env.SITE || !process.env.JWT_SECRET) {
1213
console.log('bad config')
1314
process.exit(1)
1415
}
@@ -27,7 +28,15 @@ app.use(bodyParser.json())
2728
app.use(bodyParser.urlencoded({ extended: true }))
2829

2930
app.all('/attachment/(*)', async (req, res) => {
31+
if (typeof req.query.token !== 'string') {
32+
res.status(403).send('Missing token')
33+
return
34+
}
35+
3036
try {
37+
const token = jwt.verify(req.query.token, process.env.JWT_SECRET!)
38+
if (token !== req.params[0]) throw new Error('Mismatch')
39+
3140
const { data } = await axios.get(req.params[0], {
3241
responseType: 'stream',
3342
timeout: 10_000,

0 commit comments

Comments
 (0)