All files / src webhook.ts

100% Statements 10/10
100% Branches 3/3
100% Functions 2/2
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 686x     3x                                                                                                       6x 6x   6x 6x 6x 6x 1x     5x    
import crypto from 'crypto'
 
function isValidHmacSignature(signature: string, data: Buffer, secret: string) {
  return signature === crypto.createHmac('sha256', secret).update(data).digest('hex')
}
 
export interface IsValidWebhookSignatureParams {
  /**
   * The value of the "fpjs-event-signature" header.
   * */
  header: string
  /**
   * The raw data of the incoming request
   * */
  data: Buffer
  /**
   * The secret key used to sign the request.
   * */
  secret: string
}
 
/**
 * Verifies the HMAC signature extracted from the "fpjs-event-signature" header of the incoming request. This is a part of the webhook signing process, which is available only for enterprise customers.
 * If you wish to enable it, please contact our support: https://fingerprint.com/support
 *
 * @param {IsValidWebhookSignatureParams} params
 * @param {string} params.header - The value of the "fpjs-event-signature" header.
 * @param {Buffer} params.data - The raw data of the incoming request.
 * @param {string} params.secret - The secret key used to sign the request.
 *
 * @return {boolean} true if the signature is valid, false otherwise.
 *
 * @example
 * ```javascript
 * // Webhook endpoint handler
 * export async function POST(request: Request) {
 *   try {
 *     const secret = process.env.WEBHOOK_SIGNATURE_SECRET;
 *     const header = request.headers.get("fpjs-event-signature");
 *     const data = Buffer.from(await request.arrayBuffer());
 *
 *     if (!isValidWebhookSignature({ header, data, secret })) {
 *       return Response.json(
 *         { message: "Webhook signature is invalid." },
 *         { status: 403 },
 *       );
 *     }
 *
 *     return Response.json({ message: "Webhook received." });
 *   } catch (error) {
 *     return Response.json({ error }, { status: 500 });
 *   }
 * }
 * ```
 */
export function isValidWebhookSignature(params: IsValidWebhookSignatureParams): boolean {
  const { header, data, secret } = params
 
  const signatures = header.split(',')
  for (const signature of signatures) {
    const [version, hash] = signature.split('=')
    if (version === 'v1' && isValidHmacSignature(hash, data, secret)) {
      return true
    }
  }
  return false
}