Доступ к сырому телу Stripe webhook в Nest.js - PullRequest
0 голосов
/ 24 января 2019

Мне нужно получить доступ к необработанному телу запроса webhook от Stripe в моем приложении Nest.js.

Следуя этому примеру, я добавил ниже в модуль, имеющий контроллерметод, которому требуется необработанное тело.

function addRawBody(req, res, next) {
  req.setEncoding('utf8');

  let data = '';

  req.on('data', (chunk) => {
    data += chunk;
  });

  req.on('end', () => {
    req.rawBody = data;

    next();
  });
}

export class SubscriptionModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(addRawBody)
      .forRoutes('subscriptions/stripe');
  }
}

В контроллере я использую @Req() req, а затем req.rawBody, чтобы получить необработанное тело.Мне нужно сырое тело, потому что constructEvent из API Stripe использует его для проверки запроса.

Проблема в том, что запрос застрял.Кажется, что требование не вызывается ни для данных, ни для конечного события.Так что next() не вызывается в промежуточном программном обеспечении.

Я также пытался использовать raw-body как здесь , но я получил почти такой же результат.В этом случае req.readable всегда ложно, поэтому я тоже застрял там.

Полагаю, это проблема с Nest.js, но я не уверен ...

Ответы [ 3 ]

0 голосов
/ 20 февраля 2019

Вчера вечером я столкнулся с подобной проблемой, пытаясь аутентифицировать токен Slack.

Решение, которое мы использовали, действительно требовало отключения bodyParser из основного приложения Nest и повторного включения его после добавления нового rawBody ключ к запросу с необработанным телом запроса.

    const app = await NestFactory.create(AppModule, {
        bodyParser: false
    });

    const rawBodyBuffer = (req, res, buf, encoding) => {
        if (buf && buf.length) {
            req.rawBody = buf.toString(encoding || 'utf8');
        }
    };

    app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true }));
    app.use(bodyParser.json({ verify: rawBodyBuffer }));

Тогда в моем промежуточном программном обеспечении я мог получить к нему следующий доступ:

const isVerified = (req) => {
    const signature = req.headers['x-slack-signature'];
    const timestamp = req.headers['x-slack-request-timestamp'];
    const hmac = crypto.createHmac('sha256', 'somekey');
    const [version, hash] = signature.split('=');

    // Check if the timestamp is too old
    // tslint:disable-next-line:no-bitwise
    const fiveMinutesAgo = ~~(Date.now() / 1000) - (60 * 5);
    if (timestamp < fiveMinutesAgo) { return false; }

    hmac.update(`${version}:${timestamp}:${req.rawBody}`);

    // check that the request signature matches expected value
    return timingSafeCompare(hmac.digest('hex'), hash);
};

export async function slackTokenAuthentication(req, res, next) {
    if (!isVerified(req)) {
        next(new HttpException('Not Authorized Slack', HttpStatus.FORBIDDEN));
    }
    next();
}

Shine On!

0 голосов
/ 25 июня 2019

Для тех, кто ищет более элегантное решение, отключите bodyParser в main.ts

Создайте 2 middlewares один для rawbody, а другой для json-parsed-body

json-body.middleware.ts

import {Request, Response} from 'express';
import * as bodyParser from 'body-parser';
import {Injectable, NestMiddleware} from '@nestjs/common';

@Injectable()
export class JsonBodyMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => any) {
    bodyParser.json()(req, res, next);
  }
}

raw-body.middleware.ts

import {Injectable, NestMiddleware} from '@nestjs/common';
import {Request, Response} from 'express';
import * as bodyParser from 'body-parser';

@Injectable()
export class RawBodyMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: () => any) {
    bodyParser.raw({type: '*/*'})(req, res, next);
  }
}

И применить промежуточное ПО длясоответствующие маршруты в app.module.ts.

app.module.ts

...
public configure(consumer: MiddlewareConsumer): void {
    consumer
      .apply(RawBodyMiddleware)
      .forRoutes({
        path: '/stripe-webhooks',
        method: RequestMethod.POST,
      })
      .apply(JsonBodyMiddleware)
      .forRoutes('*');
 }

BTW req.rawbody удалено из express длинный назад - https://github.com/expressjs/express/issues/897

0 голосов
/ 24 января 2019

Это связано с тем, что промежуточное ПО bodyParser используется по умолчанию и уже потребляет тело при запуске промежуточного ПО.Вы можете отключить bodyParser в вашем main.ts:

const app = await NestFactory.create(AppModule, {bodyParser: false});
                                                 ^^^^^^^^^^^^^^^^^^

Однако вы должны заметить, что в большинстве других случаев вы, вероятно, захотите использовать bodyParser.json, поэтому добавьте это ко всемдругие маршруты.Вы можете исключить один конкретный маршрут из промежуточного программного обеспечения, используя отрицательное регулярное выражение, см., Например, этот поток .

...