From 59663bd052f9d862601aefa1aba643f9e7d09c04 Mon Sep 17 00:00:00 2001 From: Guillaume Mathieu Date: Tue, 13 Apr 2021 15:46:52 -0700 Subject: [PATCH] feat: async signingSecret --- src/receivers/ExpressReceiver.spec.ts | 15 +++++++++++++-- src/receivers/ExpressReceiver.ts | 13 ++++++++++--- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/receivers/ExpressReceiver.spec.ts b/src/receivers/ExpressReceiver.spec.ts index 7ac795721..5b385b543 100644 --- a/src/receivers/ExpressReceiver.spec.ts +++ b/src/receivers/ExpressReceiver.spec.ts @@ -383,7 +383,11 @@ describe('ExpressReceiver', function () { // ---------------------------- // runWithValidRequest - async function runWithValidRequest(req: Request, state: any): Promise { + async function runWithValidRequest( + req: Request, + state: any, + signingSecretFn?: () => PromiseLike, + ): Promise { // Arrange const resp = buildResponseToVerify(state); const next = (error: any) => { @@ -391,7 +395,7 @@ describe('ExpressReceiver', function () { }; // Act - const verifier = verifySignatureAndParseRawBody(noopLogger, signingSecret); + const verifier = verifySignatureAndParseRawBody(noopLogger, signingSecretFn || signingSecret); // eslint-disable-next-line @typescript-eslint/await-thenable await verifier(req, resp, next); } @@ -410,6 +414,13 @@ describe('ExpressReceiver', function () { assert.isUndefined(state.error); }); + it('should verify requests on GCP using async signingSecret', async () => { + const state: any = {}; + await runWithValidRequest(buildGCPRequest(), state, () => Promise.resolve(signingSecret)); + // Assert + assert.isUndefined(state.error); + }); + // ---------------------------- // parse error diff --git a/src/receivers/ExpressReceiver.ts b/src/receivers/ExpressReceiver.ts index 8409f45fc..6a541c19a 100644 --- a/src/receivers/ExpressReceiver.ts +++ b/src/receivers/ExpressReceiver.ts @@ -18,7 +18,7 @@ import { renderHtmlForInstallPath } from './render-html-for-install-path'; // TODO: we throw away the key names for endpoints, so maybe we should use this interface. is it better for migrations? // if that's the reason, let's document that with a comment. export interface ExpressReceiverOptions { - signingSecret: string; + signingSecret: string | (() => PromiseLike); logger?: Logger; logLevel?: LogLevel; endpoints?: @@ -319,7 +319,10 @@ export const respondToUrlVerification: RequestHandler = (req, res, next) => { * - Verify the request signature * - Parse request.body and assign the successfully parsed object to it. */ -export function verifySignatureAndParseRawBody(logger: Logger, signingSecret: string): RequestHandler { +export function verifySignatureAndParseRawBody( + logger: Logger, + signingSecret: string | (() => PromiseLike), +): RequestHandler { return async (req, res, next) => { let stringBody: string; // On some environments like GCP (Google Cloud Platform), @@ -338,7 +341,11 @@ export function verifySignatureAndParseRawBody(logger: Logger, signingSecret: st try { // This handler parses `req.body` or `req.rawBody`(on Google Could Platform) // and overwrites `req.body` with the parsed JS object. - req.body = verifySignatureAndParseBody(signingSecret, stringBody, req.headers); + req.body = verifySignatureAndParseBody( + typeof signingSecret === 'string' ? signingSecret : await signingSecret(), + stringBody, + req.headers, + ); } catch (error) { if (error) { if (error instanceof ReceiverAuthenticityError) {