add mailgun verification
This commit is contained in:
parent
e4d671e922
commit
12737b74e3
81
lib/apis.js
81
lib/apis.js
|
@ -430,7 +430,6 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (xconfx.debug) { console.log('[api.js] twilio added'); }
|
|
||||||
var Twilio = require('twilio');
|
var Twilio = require('twilio');
|
||||||
function twilioTel(/*opts*/) {
|
function twilioTel(/*opts*/) {
|
||||||
if (_twilio) {
|
if (_twilio) {
|
||||||
|
@ -444,12 +443,76 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
|
||||||
return apiDeps.Promise.resolve(_twilio);
|
return apiDeps.Promise.resolve(_twilio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO shared memory db
|
||||||
|
var mailgunTokens = {};
|
||||||
|
function validateMailgun(apiKey, timestamp, token, signature) {
|
||||||
|
// https://gist.github.com/coolaj86/81a3b61353d2f0a2552c
|
||||||
|
// (realized later)
|
||||||
|
// HAHA HAHA HAHAHAHAHA this is my own gist... so much more polite attribution
|
||||||
|
var scmp = require('scmp')
|
||||||
|
, crypto = require('crypto')
|
||||||
|
, mailgunExpirey = 15 * 60 * 1000
|
||||||
|
, mailgunHashType = 'sha256'
|
||||||
|
, mailgunSignatureEncoding = 'hex'
|
||||||
|
;
|
||||||
|
var actual
|
||||||
|
, adjustedTimestamp = parseInt(timestamp, 10) * 1000
|
||||||
|
, fresh = (Math.abs(Date.now() - adjustedTimestamp) < mailgunExpirey)
|
||||||
|
;
|
||||||
|
|
||||||
|
if (!fresh) {
|
||||||
|
console.error('[mailgun] Stale Timestamp: this may be an attack');
|
||||||
|
console.error('[mailgun] However, this is most likely your fault\n');
|
||||||
|
console.error('[mailgun] run `ntpdate ntp.ubuntu.com` and check your system clock\n');
|
||||||
|
console.error('[mailgun] System Time: ' + new Date().toString());
|
||||||
|
console.error('[mailgun] Mailgun Time: ' + new Date(adjustedTimestamp).toString(), timestamp);
|
||||||
|
console.error('[mailgun] Delta: ' + (Date.now() - adjustedTimestamp));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mailgunTokens[token]) {
|
||||||
|
console.error('[mailgun] Replay Attack');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mailgunTokens[token] = true;
|
||||||
|
|
||||||
|
setTimeout(function () {
|
||||||
|
delete mailgunTokens[token];
|
||||||
|
}, mailgunExpirey + (5 * 1000));
|
||||||
|
|
||||||
|
actual = crypto.createHmac(mailgunHashType, apiKey)
|
||||||
|
.update(new Buffer(timestamp + token, 'utf8'))
|
||||||
|
.digest(mailgunSignatureEncoding)
|
||||||
|
;
|
||||||
|
return scmp(signature, actual);
|
||||||
|
}
|
||||||
|
|
||||||
function mailgunMail(/*opts*/) {
|
function mailgunMail(/*opts*/) {
|
||||||
return apiDeps.Promise.resolve(req.getSiteMailer());
|
return apiDeps.Promise.resolve(req.getSiteMailer());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Twilio Parameters are often 26 long
|
// Twilio Parameters are often 26 long
|
||||||
var bodyParserTwilio = require('body-parser').urlencoded({ limit: '4kb', parameterLimit: 100, extended: false });
|
var bodyParserTwilio = require('body-parser').urlencoded({ limit: '4kb', parameterLimit: 100, extended: false });
|
||||||
|
//var bodyParserMailgun = require('body-parser').urlencoded({ limit: '4kb', parameterLimit: 100, extended: false });
|
||||||
|
function bodyParserMailgun (req, res, next) {
|
||||||
|
var multiparty = require('multiparty');
|
||||||
|
var form = new multiparty.Form();
|
||||||
|
|
||||||
|
form.parse(req, function (err, fields/*, files*/) {
|
||||||
|
var body;
|
||||||
|
req.body = req.body || {};
|
||||||
|
Object.keys(fields).forEach(function (key) {
|
||||||
|
// TODO what if there were two of something?
|
||||||
|
// (even though there won't be)
|
||||||
|
req.body[key] = fields[key][0];
|
||||||
|
});
|
||||||
|
body = req.body;
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var caps = {
|
var caps = {
|
||||||
//
|
//
|
||||||
// Capabilities for APIs
|
// Capabilities for APIs
|
||||||
|
@ -463,6 +526,22 @@ module.exports.create = function (xconfx, apiFactories, apiDeps) {
|
||||||
//
|
//
|
||||||
// Webhook Parsers
|
// Webhook Parsers
|
||||||
//
|
//
|
||||||
|
, 'mailgun.urlencoded@daplie.com': function (req, res, next) {
|
||||||
|
return bodyParserMailgun(req, res, function () {
|
||||||
|
var body = req.body;
|
||||||
|
var mailconf = siteConfig['mailgun.org'];
|
||||||
|
|
||||||
|
console.log('mailgun req.headers');
|
||||||
|
console.log(req.headers);
|
||||||
|
if (!validateMailgun(mailconf.apiKey, body.timestamp, body.token, body.signature)) {
|
||||||
|
console.error('Request came, but not from Mailgun');
|
||||||
|
res.send({ error: { message: 'Invalid signature. Are you even Mailgun?' } });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
, 'twilio.urlencoded@daplie.com': function (req, res, next) {
|
, 'twilio.urlencoded@daplie.com': function (req, res, next) {
|
||||||
// TODO null for res and Promise instead of next?
|
// TODO null for res and Promise instead of next?
|
||||||
return bodyParserTwilio(req, res, function () {
|
return bodyParserTwilio(req, res, function () {
|
||||||
|
|
Loading…
Reference in New Issue