trello-node-api
Version:
Trello Node API wrapper
109 lines (85 loc) • 3.01 kB
JavaScript
var crypto = require('crypto');
var utils = require('./utils');
var Error = require('./Error');
var Webhook = {
DEFAULT_TOLERANCE: 300,
constructEvent: function (payload, header, secret, tolerance) {
var jsonPayload = JSON.parse(payload);
this.signature.verifyHeader(payload, header, secret, tolerance || Webhook.DEFAULT_TOLERANCE);
return jsonPayload;
}
};
var signature = {
EXPECTED_SCHEME: 'v1',
_computeSignature: function (payload, secret) {
return crypto.createHmac('sha256', secret)
.update(payload, 'utf8')
.digest('hex');
},
verifyHeader: function (payload, header, secret, tolerance) {
var details = parseHeader(header, this.EXPECTED_SCHEME);
if (!details || details.timestamp === -1) {
throw new Error.TrelloSignatureVerificationError({
message: 'Unable to extract timestamp and signatures from header',
detail: {
header: header,
payload: payload
}
});
}
if (!details.signatures.length) {
throw new Error.TrelloSignatureVerificationError({
message: 'No signatures found with expected scheme',
detail: {
header: header,
payload: payload
}
});
}
var expectedSignature = this._computeSignature(details.timestamp + '.' + payload, secret);
var signatureFound = !!details.signatures
.filter(utils.secureCompare.bind(utils, expectedSignature))
.length;
if (!signatureFound) {
throw new Error.TrelloSignatureVerificationError({
message: 'No signatures found matching the expected signature for payload.' +
' Are you passing the raw request body you received from Trello?',
detail: {
header: header,
payload: payload
}
});
}
var timestampAge = Math.floor(Date.now() / 1000) - details.timestamp;
if (tolerance > 0 && timestampAge > tolerance) {
throw new Error.TrelloSignatureVerificationError({
message: 'Timestamp outside the tolerance zone',
detail: {
header: header,
payload: payload
}
});
}
return true;
}
};
function parseHeader(header, scheme) {
if (typeof header !== 'string') {
return null;
}
return header.split(',').reduce(function (accum, item) {
var kv = item.split('=');
if (kv[0] === 't') {
accum.timestamp = kv[1];
}
if (kv[0] === scheme) {
accum.signatures.push(kv[1]);
}
return accum;
}, {
timestamp: -1,
signatures: []
});
}
Webhook.signature = signature;
module.exports = Webhook;