From 8e4b5f8dd6e9492e5399438f1dcbf1942f3258f1 Mon Sep 17 00:00:00 2001 From: Justin Stayton Date: Tue, 5 Dec 2023 10:19:47 -0500 Subject: [PATCH] Tweaks --- README.md | 45 +++++++++++++++++++++++++-------------------- package.json | 2 +- src/main.js | 11 +++++++++-- src/main.test.js | 32 ++++++++++++++++---------------- 4 files changed, 51 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 6b1ee2a..2ebb4e3 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,18 @@ -# Truepic Webhook Verifier for Node.js +

+ Truepic Webhook Verifier for Node.js +

-Verify webhooks from Truepic Vision or Lens in your Node.js app. +

+ Verify webhooks from Truepic Vision or Lens in your Node.js app +

This module verifies - the integrity of the data being received, - the authenticity of the sender (Truepic), - the authenticity of the receiver (you), and -- the time between the request being sent and received to prevent replay attack. +- the time between the request being sent and received to prevent replay + attacks. If you're not using Node.js, this also serves as a reference implementation with thorough documentation to make the translation into another language as painless @@ -21,17 +26,18 @@ npm install @truepic/webhook-verifier ## Usage -The `@truepic/webhook-verifier` module exports a named `verifyWebhook` function -that should be imported to begin: +The `@truepic/webhook-verifier` module exports a default function that should be +imported to begin: ```js -import { verifyWebhook } from '@truepic/webhook-verifier' +import verifyTruepicWebhook from '@truepic/webhook-verifier' ``` -This `verifyWebhook` function is then called with the following arguments: +This `verifyTruepicWebhook` function (or whatever you imported it as) is then +called with the following arguments: ```js -verifyWebhook({ +verifyTruepicWebhook({ url: 'The full URL that received the request and is registered with Truepic.', secret: "The shared secret that's registered with Truepic.", header: 'The value of the `truepic-signature` header from the request.', @@ -54,7 +60,7 @@ to adapt if you're using a different one. ### Example: Express.js ```js -import { verifyWebhook } from '@truepic/webhook-verifier' +import verifyTruepicWebhook from '@truepic/webhook-verifier' import express from 'express' import { env } from 'node:process' @@ -62,13 +68,13 @@ const app = express() app.post( '/truepic/webhook', - // This is important! We need the raw request body for `verifyWebhook`. + // This is important! We need the raw request body for `verifyTruepicWebhook`. express.raw({ type: 'application/json', }), (req, res, next) => { try { - verifyWebhook({ + verifyTruepicWebhook({ url: env.TRUEPIC_WEBHOOK_URL, secret: env.TRUEPIC_WEBHOOK_SECRET, header: req.header('truepic-signature'), @@ -99,7 +105,7 @@ npm install fastify-raw-body ``` ```js -import { verifyWebhook } from '@truepic/webhook-verifier' +import verifyTruepicWebhook from '@truepic/webhook-verifier' import Fastify from 'fastify' import { env } from 'node:process' @@ -107,12 +113,12 @@ const app = Fastify({ logger: true, }) -// This is important! We need the raw request body for `verifyWebhook`. +// This is important! We need the raw request body for `verifyTruepicWebhook`. await app.register(import('fastify-raw-body')) app.post('/truepic/webhook', async (request) => { try { - verifyWebhook({ + verifyTruepicWebhook({ url: env.TRUEPIC_WEBHOOK_URL, secret: env.TRUEPIC_WEBHOOK_SECRET, header: request.headers['truepic-signature'], @@ -200,14 +206,13 @@ npm run lint ### Releasing -After development is done in the `development` branch and is ready for release, -it should be merged into the `main` branch, where the latest release code lives. -[Release It!](https://github.com/release-it/release-it) is then used to -orchestrate the release process: +When the `development` branch is ready for release, +[Release It!](https://github.com/release-it/release-it) is used to orchestrate +the release process: ```bash npm run release ``` -Once the release process is complete, merge the `main` branch back into the -`development` branch. They should have the same history at this point. +Once the release process is complete, merge the `development` branch into the +`main` branch, which should always reflect the latest release. diff --git a/package.json b/package.json index 095f86d..f531fbc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@truepic/webhook-verifier", - "version": "0.1.0", + "version": "1.0.0", "type": "module", "description": "Verify webhooks from Truepic Vision or Lens in your Node.js app", "homepage": "https://github.com/TRUEPIC/webhook-verifier-nodejs#readme", diff --git a/src/main.js b/src/main.js index ea426f5..42a8ef0 100644 --- a/src/main.js +++ b/src/main.js @@ -138,7 +138,13 @@ function verifySignature({ url, secret, body, timestamp, signature }) { * @throws {TruepicWebhookVerifierError} If verification fails. * @returns {true} If verification succeeds. */ -function verifyWebhook({ url, secret, header, body, leewayMinutes = 5 }) { +function verifyTruepicWebhook({ + url, + secret, + header, + body, + leewayMinutes = 5, +}) { const { timestamp, signature } = parseHeader(header) verifyTimestamp({ @@ -158,4 +164,5 @@ function verifyWebhook({ url, secret, header, body, leewayMinutes = 5 }) { } /** @module @truepic/webhook-verifier */ -export { verifyWebhook, TruepicWebhookVerifierError } +export default verifyTruepicWebhook +export { TruepicWebhookVerifierError } diff --git a/src/main.test.js b/src/main.test.js index 43736c2..90a56a6 100644 --- a/src/main.test.js +++ b/src/main.test.js @@ -1,8 +1,8 @@ import assert from 'node:assert/strict' import { describe, it } from 'node:test' -import { verifyWebhook, TruepicWebhookVerifierError } from './main.js' +import verifyTruepicWebhook, { TruepicWebhookVerifierError } from './main.js' -describe('verifyWebhook', () => { +describe('verifyTruepicWebhook', () => { // Successful values. const url = 'http://localhost:3001/webhook' const secret = 'secret' @@ -13,7 +13,7 @@ describe('verifyWebhook', () => { it('returns `true` if verification is successful', () => { assert.strictEqual( - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header, @@ -28,7 +28,7 @@ describe('verifyWebhook', () => { it('if the `header` is missing', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: null, @@ -42,7 +42,7 @@ describe('verifyWebhook', () => { it('if the `header` is empty', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: '', @@ -56,7 +56,7 @@ describe('verifyWebhook', () => { it('if the `header` cannot be parsed into timestamp and signature', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 'bad', @@ -72,7 +72,7 @@ describe('verifyWebhook', () => { it('if the `header` is missing the timestamp (`t`)', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 'b=bad,s=test', @@ -86,7 +86,7 @@ describe('verifyWebhook', () => { it('if the `header` timestamp (`t`) is empty', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 't=,s=test', @@ -100,7 +100,7 @@ describe('verifyWebhook', () => { it('if the `header` timestamp (`t`) is not a number', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 't=bad,s=test', @@ -114,7 +114,7 @@ describe('verifyWebhook', () => { it('if the `header` is missing the signature (`s`)', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 't=123,b=bad', @@ -128,7 +128,7 @@ describe('verifyWebhook', () => { it('if the `header` signature (`s`) is empty', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: 't=123,s=', @@ -142,7 +142,7 @@ describe('verifyWebhook', () => { it('if the timestamp is not within allowed window', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header, @@ -158,7 +158,7 @@ describe('verifyWebhook', () => { it('if the `url` is not where the request was sent', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url: 'http://bad/webhook', secret, header, @@ -172,7 +172,7 @@ describe('verifyWebhook', () => { it('if the `timestamp` is not what was signed', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header: header.replace('t=1698259719', 't=1698259718'), @@ -186,7 +186,7 @@ describe('verifyWebhook', () => { it('if the `body` is not what was signed', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret, header, @@ -200,7 +200,7 @@ describe('verifyWebhook', () => { it('if the `secret` is not what was used to sign', () => { assert.throws( () => - verifyWebhook({ + verifyTruepicWebhook({ url, secret: 'bad', header,