在Nest.js中访问Stripe webhook的原始主体

问题描述 投票:2回答:3

我需要在我的Nest.js应用程序中从Stripe访问webhook请求的原始主体。

this示例之后,我将以下内容添加到具有需要原始主体的控制器方法的模块中。

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() reqand然后使用req.rawBody来获取原始体。我需要原始主体,因为Stripe api的constructEvent正在使用它来验证请求。

问题是请求被卡住了。似乎req.on既不是数据也不是结束事件。所以next()不是在中间件中调用的。

我也尝试使用像raw-body这样的here,但我得到了相同的结果。在那种情况下,req.readable总是假的,所以我也被困在那里。

我想这是Nest.js的一个问题,但我不确定......

node.js typescript express stripe-payments nestjs
3个回答
5
投票

我昨晚遇到了类似的问题,试图验证一个Slack令牌。

我们使用的解决方案确实需要从核心Nest App禁用bodyParser,然后在使用原始请求主体向请求添加新的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();
}

闪耀!


1
投票

对于寻找更优雅解决方案的人,请关闭bodyParser中的main.ts

为rawbody创建2个middlewares,为json-parsed-body创建另一个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); } }

JSON-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);
  }
}

原始body.middleware.ts

app.module.ts

并将中间件应用于... public configure(consumer: MiddlewareConsumer): void { consumer .apply(RawBodyMiddleware) .forRoutes({ path: '/stripe-webhooks', method: RequestMethod.POST, }) .apply(JsonBodyMiddleware) .forRoutes('*'); } 中的适当路线。

app.module.ts

req.rawbody

BTW express已经从https://github.com/expressjs/express/issues/897长期被移除 - bodyParser


-1
投票

这是因为默认情况下使用bodyParser中间件,并且在中间件启动时已经消耗了主体。你可以在你的main.ts关闭const app = await NestFactory.create(AppModule, {bodyParser: false}); ^^^^^^^^^^^^^^^^^^

bodyParser.json

您应该注意,在大多数其他情况下,您可能希望使用this thread,因此将此添加到所有其他路由。您可以使用负正则表达式从中间件中排除一个特定路由,请参阅例如qazxswpoi。

© www.soinside.com 2019 - 2024. All rights reserved.