8

Is there a way to access the raw body of the request? Not that has been parsed into json?

@Injectable()
export class WooGuard implements CanActivate {
  secret: string;

  constructor(
    private readonly reflector: Reflector,
    private configService: ConfigService,
    ) {
      this.secret = this.configService.get<string>("woocommerce.webhook.secret");
    }

  async canActivate(
    context: ExecutionContext,
    ): Promise<boolean> {

    const request = context.switchToHttp().getRequest<Request>();
    request.body // this is parsed json

    // I am calculating the sha256 hash of the body with a secret for a webhook.
    // due to how the raw json is vs. the JSON.stringify(request.body), the signature is never the same.
  }
}
metodribic
  • 1,561
  • 17
  • 27
dre
  • 1,422
  • 3
  • 21
  • 31
  • 1
    @dreadjr I'm building webhook integrations libraries for NestJS for a couple different third party services. This issue comes up a lot, so I've created a library to simplify the foundation of webhook integrations: https://www.npmjs.com/package/@golevelup/nestjs-webhooks There are simple instructions here for how to get raw body parsing setup with minimal effort npm @golevelup/nestjs-webhooks Badass utilities for integrating webhooks and NestJS – dre Apr 07 '20 at 15:22
  • @dreadjr Would this solution work for you? https://stackoverflow.com/a/54788734/872328 Stack Overflow Access raw body of Stripe webhook in Nest.js I need to access the raw body of the webhook request from Stripe in my Nest.js application. Following this example, I added the below to the module which has a controller method that is needing th... – dre Apr 07 '20 at 15:23
  • Does this answer your question? [Access raw body of Stripe webhook in Nest.js](https://stackoverflow.com/questions/54346465/access-raw-body-of-stripe-webhook-in-nest-js) – Andrew Radulescu Sep 08 '21 at 14:31

3 Answers3

5

You can add a middleware in main.ts (after app was initialized) to prevent NestJS from parsing the body to JSON for a specific route you need.

import { raw } from 'body-parser'

const app = await NestFactory.create(AppModule);

// Will keep the raw body
app.use('/users', raw({type: 'application/json'}));

All requests under the /users route will have the raw body.

Pro: fast and easy for 1-2 endpoints, testing, etc.
Cons: may become cumbersome for many routes. Can disable body-parser instead and create 2 functional middlewares (one for raw and one for JSON parse).

Andrew Radulescu
  • 1,862
  • 13
  • 21
3

Shopify has a similar way to validate requests, this code worked for me, maybe you can change it.

First you need to install crypto:

npm install --save crypto

Then:

import { Injectable, CanActivate, ExecutionContext, HttpStatus } from '@nestjs/common';
const crypto = require('crypto');

@Injectable()
export class ShopifyAuthGuard implements CanActivate {
  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const secretKey = <MY_KEY>;

    const hmac = request.headers["x-shopify-hmac-sha256"];

    const hash = crypto
      .createHmac('sha256', secretKey)
      .update(request.body)
      .digest('base64');

    if (hmac === hash) {
      return true;
    } else {
      throw new ForbiddenException("Not allowed");
    }
  }
}

And finally on your controller:

@Post()
@UseGuards(ShopifyAuthGuard)
async createNewOrder(@Body() orderDto: OrderDto) {}

Hope it helps!

arceliver
  • 336
  • 1
  • 11
  • 1
    It was a problem with the body-parser not the calculation of the hash. But thank you for the contribution. – dre Apr 07 '20 at 15:22
  • Got it, I think that this code needs to use raw body too, and this is how I made it to work, thats why I shared with you. Good luck! – arceliver Apr 07 '20 at 17:33
  • 1
    yeah that was the key, using the rawbody, or in nestjs case, accessing the raw body. Which was more difficult than it should be. – dre Apr 07 '20 at 17:57
  • 2
    Hi - please can you post the full working solution with the raw body? – Ed Stephenson Mar 05 '21 at 14:49
  • This works with simple text message, but failed when we add emojis in the text – zubair Irfan Jul 26 '23 at 10:08
0

Adding these options in Main.ts worked for me

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

And in the guard, you can access raw body as:

const request = context.switchToHttp().getRequest();
const rawBody = request.rawBody;

Or in the controller, you can access it as:

  @Post("webhooks")
  async webhooks(
  @Req() req: RawBodyRequest<Request>,
  @Res() res: Response,
  ) {
    // do stuff with it
zubair Irfan
  • 103
  • 12