'use strict'; module.exports.create = function (deps) { var PromiseA = require('bluebird'); var request = PromiseA.promisify(require('request')); var pending = {}; function checkPublicAddr(host) { return request({ method: 'GET' , url: host+'/api/org.oauth3.tunnel/checkip' , json: true }).then(function (result) { if (!result.body) { return PromiseA.reject(new Error('No response body in request for public address')); } if (result.body.error) { var err = new Error(result.body.error.message); return PromiseA.reject(Object.assign(err, result.body.error)); } return result.body.address; }); } function checkSinglePort(host, address, port) { var crypto = require('crypto'); var token = crypto.randomBytes(8).toString('hex'); var keyAuth = crypto.randomBytes(32).toString('hex'); pending[token] = keyAuth; var opts = { address: address , port: port , token: token , keyAuthorization: keyAuth , iat: Date.now() }; return request({ method: 'POST' , url: host+'/api/org.oauth3.tunnel/loopback' , json: opts }) .then(function (result) { delete pending[token]; if (!result.body) { return PromiseA.reject(new Error('No response body in loopback request for port '+port)); } // If the loopback requests don't go to us then there are all kinds of ways it could // error, but none of them really provide much extra information so we don't do // anything that will break the PromiseA.all out and mask the other results. if (result.body.error) { console.log('error on remote side of port '+port+' loopback', result.body.error); } return !!result.body.success; }, function (err) { delete pending[token]; throw err; }); } function loopback(provider) { return deps.OAUTH3.discover(provider).then(function (directives) { return checkPublicAddr(directives.api).then(function (address) { console.log('checking to see if', address, 'gets back to us'); var ports = require('./servers').listeners.tcp.list(); return PromiseA.all(ports.map(function (port) { return checkSinglePort(directives.api, address, port); })) .then(function (values) { console.log(pending); var result = {error: null, address: address}; ports.forEach(function (port, ind) { result[port] = values[ind]; }); return result; }); }); }); } loopback.server = require('http').createServer(function (req, res) { var parsed = require('url').parse(req.url); var token = parsed.pathname.replace('/.well-known/cloud-challenge/', ''); if (pending[token]) { res.setHeader('Content-Type', 'text/plain'); res.end(pending[token]); } else { res.statusCode = 404; res.end(); } }); return loopback; };