tyo-mq
Version:
Distributed Message Pub/Sub Service with socket.io
74 lines (61 loc) • 2.26 kB
JavaScript
/**
* Signed manager actions for admin-only operations.
*/
;
const crypto = require('crypto');
function stableStringify(value) {
if (value === null || typeof value !== 'object')
return JSON.stringify(value);
if (Array.isArray(value))
return '[' + value.map(stableStringify).join(',') + ']';
return '{' + Object.keys(value).sort().map(function (key) {
return JSON.stringify(key) + ':' + stableStringify(value[key]);
}).join(',') + '}';
}
function signatureBase(action, body, timestamp, nonce) {
return [
String(action || ''),
String(timestamp || ''),
String(nonce || ''),
stableStringify(body || {})
].join('\n');
}
function signAdminAction(adminToken, action, body, timestamp, nonce) {
return crypto.createHmac('sha256', String(adminToken))
.update(signatureBase(action, body, timestamp, nonce))
.digest('hex');
}
function createAdminProof(adminToken, action, body, opts) {
opts = opts || {};
var timestamp = opts.timestamp || Date.now();
var nonce = opts.nonce || crypto.randomBytes(16).toString('hex');
return {
timestamp: timestamp,
nonce: nonce,
signature: signAdminAction(adminToken, action, body || {}, timestamp, nonce)
};
}
function signaturesEqual(a, b) {
var left = Buffer.from(String(a || ''), 'hex');
var right = Buffer.from(String(b || ''), 'hex');
return left.length === right.length && crypto.timingSafeEqual(left, right);
}
function verifyAdminProof(adminToken, action, body, proof, opts) {
opts = opts || {};
if (!proof || !proof.timestamp || !proof.nonce || !proof.signature)
return false;
var maxAgeMs = opts.maxAgeMs || 5 * 60 * 1000;
var timestamp = Number(proof.timestamp);
if (!Number.isFinite(timestamp))
return false;
if (Math.abs(Date.now() - timestamp) > maxAgeMs)
return false;
var expected = signAdminAction(adminToken, action, body || {}, timestamp, proof.nonce);
return signaturesEqual(expected, proof.signature);
}
module.exports = {
createAdminProof: createAdminProof,
signAdminAction: signAdminAction,
stableStringify: stableStringify,
verifyAdminProof: verifyAdminProof
};