vhook-js
Version:
Verifyable Webhooks
119 lines (99 loc) • 4.9 kB
JavaScript
// test/vhook-import.test.js
import assert from 'assert';
import vhook from '../vhook.mjs'; // Adjust the path as necessary
import jwt from 'jsonwebtoken';
describe('VHooks Module Import Test', function () {
let keys, privateKey, publicKey, now, payload, token;
before(async function () {
// Generate key pair before running tests
keys = await vhook.create_vhook_keypair();
privateKey = keys.privateKey.pem;
publicKey = keys.publicKey.pem;
now = Math.floor(Date.now()/1000);
});
describe('vhook import', function () {
it('should have all the expected functions', function () {
assert.ok(typeof vhook.create_vhook_keypair === 'function', 'create_vhook_keypair is not a function');
assert.ok(typeof vhook.create_vhook === 'function', 'create_vhook is not a function');
assert.ok(typeof vhook.decode_vhook === 'function', 'decode_vhook is not a function');
assert.ok(typeof vhook.decode_vhook_without_validation === 'function', 'decode_vhook_without_validation is not a function');
assert.ok(typeof vhook.prepare_vhook_payload === 'function', 'prepare_vhook_payload is not a function');
assert.ok(typeof vhook.extract_vhook_payload === 'function', 'extract_vhook_payload is not a function');
});
});
describe('create_vhook_keypair()', function () {
it('should generate a valid RSA key pair', function () {
assert.ok(keys.privateKey);
assert.ok(keys.publicKey);
assert.ok(keys.privateKey.pem);
assert.ok(keys.publicKey.pem);
// Check that keys are in PEM format
assert.ok(keys.privateKey.pem.includes('-----BEGIN RSA PRIVATE KEY-----'));
assert.ok(keys.publicKey.pem.includes('-----BEGIN PUBLIC KEY-----'));
});
});
describe('create_vhook()', function () {
it('should create a signed VHook token', function () {
payload = {
issuer: 'https://yourservice.com',
audience: 'https://receiver-service.com/webhook-endpoint',
origin: 'customer',
event: 'customer.updated',
data: {
customer_id: '12345',
name: 'Jane Doe',
email: 'jane.doe@example.com',
},
issued_at: now,
expiration: now + 60, // Expires in 60 seconds
message_id: 'test-message-id',
};
token = vhook.create_vhook(payload, privateKey);
assert.strictEqual(typeof token, 'string');
// Token should have three parts separated by dots
assert.strictEqual(token.split('.').length, 3);
});
it('should include msg_id in the payload', function () {
const decoded = jwt.decode(token);
assert.ok(decoded.jti);
assert.strictEqual(typeof decoded.jti, 'string');
});
});
describe('decode_vhook()', function () {
it('should decode and verify the VHook token', function () {
const decodedPayload = vhook.decode_vhook(token, publicKey);
// Check that the decoded payload matches the original payload
assert.deepStrictEqual(decodedPayload.origin, payload.origin);
assert.deepStrictEqual(decodedPayload.event, payload.event);
assert.deepStrictEqual(decodedPayload.data, payload.data);
// Check that standard claims are present
assert.strictEqual(decodedPayload.issuer, payload.issuer);
assert.strictEqual(decodedPayload.audience, payload.audience);
// Compare date fields as Date objects
assert.deepStrictEqual(decodedPayload.expiration, new Date(payload.expiration * 1000));
assert.deepStrictEqual(decodedPayload.issued_at, new Date(payload.issued_at * 1000));
// Ensure message_id is present and matches
assert.strictEqual(decodedPayload.message_id, payload.message_id);
});
});
describe('decode_vhook_without_validation()', function () {
it('should decode the VHook token without verifying the signature', function () {
const decodedPayload = vhook.decode_vhook_without_validation(token);
assert.deepStrictEqual(decodedPayload.origin, payload.origin);
assert.deepStrictEqual(decodedPayload.event, payload.event);
assert.deepStrictEqual(decodedPayload.data, payload.data);
assert.ok(decodedPayload.message_id);
});
it('should decode a tampered token without error', function () {
// Use the previously tampered token
const parts = token.split('.');
const tamperedPayload = Buffer.from(parts[1], 'base64').toString('utf8');
const tamperedPayloadObj = JSON.parse(tamperedPayload);
tamperedPayloadObj.origin = 'attacker';
const newPayload = Buffer.from(JSON.stringify(tamperedPayloadObj)).toString('base64').replace(/=/g, '');
const tamperedToken = `${parts[0]}.${newPayload}.${parts[2]}`;
const decodedPayload = vhook.decode_vhook_without_validation(tamperedToken);
assert.strictEqual(decodedPayload.origin, 'attacker');
});
});
});