Skip to content

Instantly share code, notes, and snippets.

@jcoglan
Created September 20, 2012 20:02
Show Gist options
  • Save jcoglan/3758017 to your computer and use it in GitHub Desktop.
Save jcoglan/3758017 to your computer and use it in GitHub Desktop.
var child = require('child_process'),
fs = require('fs'),
http = require('http'),
qs = require('querystring'),
url = require('url'),
Proxy = require('./proxy');
var extractToken = function(query, callback) {
var params = qs.parse(query),
token = params.access_token,
error = params.hasOwnProperty('error') ? new Error(params.error_description) : null;
if (error) error.type = params.error;
callback(error, token);
};
var authenticate = function(target, clientId, scopes, callback) {
var uri = url.parse(target, true),
inline = (process.env.INLINE !== undefined),
browser,
server;
var receiver = http.createServer(function(request, response) {
Proxy.buffer(request, function() {
if (request.url !== '/') {
response.writeHead(404, {});
return response.end();
}
if (request.method === 'GET') {
fs.readFile(__dirname + '/receive.html', function(error, content) {
response.writeHead(200, {'Content-Type': 'text/html'});
response.end(content, 'utf-8');
});
}
else if (request.method === 'POST') {
response.writeHead(200, {});
response.end();
receiver.close();
extractToken(request.body, callback);
}
});
});
var proxy = Proxy.createPool(function(res) {
var hash = url.parse(res.headers.location || '').hash || '';
if (res.statusCode < 301 || res.statusCode > 303 || !/\baccess_token=/.test(hash))
return false;
browser.kill();
proxy.close();
extractToken(hash.substr(1), callback);
return true;
});
server = inline ? proxy.forOrigin(uri.protocol + '//' + uri.host) : receiver;
server.listen(0, function() {
var addr = server.address(),
client = 'http://' + addr.address + ':' + addr.port + '/',
params = {client_id: clientId, redirect_uri: client, response_type: 'token', scope: scopes.join(' ')};
var providerUrl = inline
? 'http://' + addr.address + ':' + addr.port + uri.pathname + (uri.search || '')
: target;
providerUrl += '?' + qs.stringify(params);
var cmds = {win32: 'cmd', darwin: 'open', other: 'xdg-open'},
command = process.env.CMD || cmds[process.platform] || cmds.other,
args = (command === 'cmd') ? ['/c', 'start', '""', providerUrl] : [providerUrl],
options = {};
if (inline) options.stdio = [0,2,2];
browser = child.spawn(command, args, options);
});
};
// e.g.
authenticate('https://5apps.com/rs/oauth/jcoglan', 'vault', ['vault:rw'], function(error, token) {
console.log('TOKEN', token);
process.exit();
});
var http = require('http'),
https = require('https'),
url = require('url');
var Proxy = function(pool, origin) {
var uri = url.parse(origin);
this._server = http.createServer(function(request, response) {
var client = (uri.protocol === 'https:') ? https : http,
requri = url.parse(request.url);
request.headers.host = uri.host;
var req = client.request({
host: uri.host,
port: uri.port,
method: request.method,
path: requri.pathname + (requri.search || ''),
headers: request.headers
});
req.addListener('response', function(res) {
Proxy.buffer(res, function() {
pool.rewrite(res, function() {
if (pool.filter(res)) return;
delete res.headers['transfer-encoding'];
response.writeHead(res.statusCode, res.headers);
response.end(res.body);
});
});
});
request.pipe(req);
});
};
['listen', 'close', 'address'].forEach(function(method) {
Proxy.prototype[method] = function() {
return this._server[method].apply(this._server, arguments);
};
});
var Pool = function(filter) {
this._filter = filter;
this._proxies = {};
};
Pool.prototype.close = function() {
for (var origin in this._proxies) this._proxies[origin].close();
};
Pool.prototype.filter = function(response) {
return this._filter(response);
};
Pool.prototype.forOrigin = function(origin, filter) {
this._proxies[origin] = this._proxies[origin] || new Proxy(this, origin);
return this._proxies[origin];
};
Pool.prototype.listen = function(origin, callback) {
var proxy = this.forOrigin(origin),
addr = proxy.address();
if (addr) return callback('http://' + addr.address + ':' + addr.port);
proxy.listen(0, function() {
var addr = proxy.address();
callback('http://' + addr.address + ':' + addr.port);
});
};
Pool.prototype.rewrite = function(response, callback) {
var location = response.headers.location;
if (location === undefined) return callback();
var uri = url.parse(location),
origin = uri.protocol + '//' + uri.host;
if (uri.hostname === '0.0.0.0') return callback();
this.listen(origin, function(host) {
response.headers.location = host + uri.pathname + (uri.search || '');
callback();
});
};
Proxy.buffer = function(stream, callback) {
var body = '';
stream.setEncoding('utf-8');
stream.addListener('data', function(c) { body += c });
stream.addListener('end', function() {
stream.body = body;
callback();
});
};
Proxy.createPool = function(filter) {
return new Pool(filter);
};
module.exports = Proxy;
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Receive token</title>
</head>
<body>
<p id="stat">Saving access token&hellip;</p>
<script type="text/javascript">
var xhr = window.ActiveXObject
? new ActiveXObject("Microsoft.XMLHTTP")
: new XMLHttpRequest();
xhr.open('POST', '/', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
var status = document.getElementById('stat');
status.innerHTML = 'You have successfully authenticated. Please close ' +
'this window and return to your shell.';
};
xhr.send(window.location.hash.substr(1));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment