🐥

LINE Chatbot SDKのvalidateSignatureでエラー

2022/09/29に公開

NestJSでLINE Chatbotのハンドラーを作っています。

Webhookを処理するときには署名を検証しなければならないので、NestJSの御作法にしたがってGuardに署名検証処理を実装しました。

  canActivate(context: ExecutionContext): boolean {
    const secret = getLineChannelSecret();
    const req = context.switchToHttp().getRequest<Request>();

    const header = req.headers[LINE_SIGNATURE_HEADER_NAME];
    if (typeof header !== "string") {
      return false;
    }

    return validateSignature(req.body, secret, header);
  }

デプロイして実際にLINEからWebhookを受け取ってみるとこんなエラーが。

TypeError [ERR_INVALID_ARG_TYPE]: The "data" argument must be of type string or an instance of Buffer, TypedArray, or DataView. Received an instance of Object

調べてみると、まさにドンピシャなものが公式にありました。
https://docs.nestjs.com/faq/raw-body

One of the most common use-case for having access to the raw request body is performing webhook signature verifications. Usually to perform webhook signature validations the unserialized request body is required to calculate an HMAC hash.

ということで、生のリクエストボディを署名検証関数に渡す必要がありそうです。

Expressを使っているので、公式のFAQのとおりに、

const app = await NestFactory.create(AppModule, {
  rawBody: true,
});

オプションで rawBody を有効化。

そしてGuardは次のように変更。

  canActivate(context: ExecutionContext): boolean {
    const secret = getLineChannelSecret();
    const req = context.switchToHttp().getRequest<RawBodyRequest<Request>>();

    const header = req.headers[LINE_SIGNATURE_HEADER_NAME];
    if (typeof header !== "string") {
      return false;
    }

    return validateSignature(req.rawBody!, secret, header);
  }

これで無事解決。

Discussion