Skip to content

Commit

Permalink
Merge pull request #2 from Ry0xi/feature/middleware-discord-authoriza…
Browse files Browse the repository at this point in the history
…tion

Feature/middleware discord authorization
  • Loading branch information
Ry0xi authored Dec 18, 2023
2 parents 481ca3e + 38fe2bd commit 9efdf62
Show file tree
Hide file tree
Showing 9 changed files with 1,251 additions and 8 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
SSM_PREFIX=discord-interactions-middlewares
DISCORD_PUBLIC_KEY=0000000000000
1 change: 1 addition & 0 deletions bin/discord-interactions-middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ new DiscordInteractionsMiddlewaresStack(
{
env,
ssmPrefix: getRequiredEnv('SSM_PREFIX'),
discordPublicKey: getRequiredEnv('DISCORD_PUBLIC_KEY'),
},
);
40 changes: 32 additions & 8 deletions handlers/discord-bot-handler/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,57 @@ import type {
APIGatewayProxyEventV2,
APIGatewayProxyResultV2,
} from 'aws-lambda';
import { InteractionResponseType, InteractionType } from 'discord-interactions';

import type { DiscordInteractionEvent } from '@/handlers/interaction-event-schema';
import discordAuthorizationMiddleware from '@/handlers/middlewares/discord-authorization';
import discordHandlePingMessageMiddleware from '@/handlers/middlewares/discord-handle-ping-message';
import { getEnv } from '@/handlers/utils';

const handleInteraction = async (
event: APIGatewayProxyEventV2,
event: DiscordInteractionEvent,
): Promise<APIGatewayProxyResultV2> => {
console.log('Start handling interaction.');
console.log('SSM_PREFIX:', getEnv('SSM_PREFIX'));
console.log('event:', event);
if (event.body.type === InteractionType.APPLICATION_COMMAND) {
return {
statusCode: 200,
body: JSON.stringify({
type: InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data: {
content:
event.body.data?.options?.[0]?.value ??
'you can type any text.',
},
}),
headers: {
'Content-Type': 'application/json',
},
};
}

return {
statusCode: 200,
body: 'ok',
statusCode: 400,
body: JSON.stringify({
message: 'Bad Request',
}),
};
};

export const handler = middy()
export const handler = middy<APIGatewayProxyEventV2>()
// input and output logging
// https://middy.js.org/docs/middlewares/input-output-logger/
.use(inputOutputLoggerMiddleware())
// normalize HTTP headers to lowercase
// https://middy.js.org/docs/middlewares/http-header-normalizer
.use(httpHeaderNormalizerMiddleware())
// add raw body for discord-authorization
.before((request) => {
(
request.event as APIGatewayProxyEventV2 & { rawBody?: string }
).rawBody = request.event.body;
})
// parse HTTP request body and convert it into an object
// https://middy.js.org/docs/middlewares/http-json-body-parser
.use(httpJsonBodyParserMiddleware())
.use(discordAuthorizationMiddleware())
.use(discordHandlePingMessageMiddleware())
// handle uncaught errors that contain the properties statusCode and message and creates a proper HTTP response for them
// https://middy.js.org/docs/middlewares/http-error-handler
Expand Down
1 change: 1 addition & 0 deletions handlers/interaction-event-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { InteractionType } from 'discord-interactions';
export interface DiscordInteractionEvent
extends Omit<APIGatewayProxyEventV2, 'body'> {
body: InteractionBodyType;
rawBody: string;
}

export interface InteractionBodyType {
Expand Down
44 changes: 44 additions & 0 deletions handlers/middlewares/discord-authorization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type middy from '@middy/core';
import { createError } from '@middy/util';
import type { APIGatewayProxyResult } from 'aws-lambda';
import { verifyKey } from 'discord-interactions';

import type { DiscordInteractionEvent } from '@/handlers/interaction-event-schema';
import { getEnv, getParameter } from '@/handlers/utils';

const discordAuthorizationMiddleware = (): middy.MiddlewareObj<
DiscordInteractionEvent,
APIGatewayProxyResult
> => {
/**
* Discord Authorization
*
* @see https://discord.com/developers/docs/interactions/receiving-and-responding#security-and-authorization
*/
const discordAuthorizationMiddlewareBefore: middy.MiddlewareFn<
DiscordInteractionEvent,
APIGatewayProxyResult
> = async (request): Promise<APIGatewayProxyResult | void> => {
const headers = request.event.headers;
const signature = headers['x-signature-ed25519'];
const timestamp = headers['x-signature-timestamp'];
const publicKey = await getParameter(
`/${getEnv('SSM_PREFIX')}/discordPublicKey`,
);

if (
!signature ||
!timestamp ||
!publicKey ||
!verifyKey(request.event.rawBody, signature, timestamp, publicKey)
) {
throw createError(401, 'discord authorization failed.');
}
};

return {
before: discordAuthorizationMiddlewareBefore,
};
};

export default discordAuthorizationMiddleware;
Loading

0 comments on commit 9efdf62

Please sign in to comment.