Webhook signature verification
Verify that a webhook request comes from Subrite by computing an HMAC SHA256 of the timestamp and payload with your webhook secret. Includes a NestJS example.
Verify that a webhook request comes from Subrite by computing an HMAC SHA256 of the timestamp and payload with your webhook secret. Includes a NestJS example.
Upon receiving the request at your endpoint, it is recommended to verify the signature to ensure the request's authenticity and integrity. This can be done by comparing the x-subrite-webhook-signature header with a computed signature based on your webhook secret and the request payload. The x-subrite-webhook-signature-timestamp header can be used to prevent replay attacks by ensuring the request is within an acceptable time frame.
Here is a basic outline of the steps for signature verification:
x-subrite-webhook-signaturex-subrite-webhook-signature-timestampBy implementing these steps, you can ensure that the request is genuinely from Subrite and has not been tampered with during transmission.
Below is a sample implementation in TypeScript for verifying the signature of incoming webhook requests in a NestJS application. This example demonstrates how to extract the signature and timestamp from the request headers, compute the HMAC signature, and compare it to the received signature.
import { Controller, Post, Body, Headers, HttpException, HttpStatus } from '@nestjs/common';
import * as crypto from 'crypto';
@Controller('webhook')
export class WebhookController {
@Post('test')
testWebhook(@Body() body: unknown, @Headers() headers: Record<string, string>) {
const signature = headers['x-subrite-webhook-signature'] as string;
const timestamp = headers['x-subrite-webhook-signature-timestamp'] as string;
if (!this.verifySignature(signature, timestamp, JSON.stringify(body))) {
throw new HttpException('Invalid signature', HttpStatus.UNAUTHORIZED);
}
// Implement your logic
}
verifySignature(signature: string, timestamp: string, body: string = '') {
const secret = 'your-webhook-secret';
const algorithm = 'sha256';
const computedSignature = this.computeSignature(secret, algorithm, timestamp, body);
return computedSignature === signature;
}
computeSignature(
secret: string,
algorithm: string,
timestamp: string,
body: string = '',
): string {
const data = body || '';
const hmac = crypto.createHmac(algorithm, secret);
const signature = hmac.update(timestamp + data).digest('base64');
return signature;
}
}