232 lines
7.1 KiB
JavaScript
232 lines
7.1 KiB
JavaScript
var https = require('https'),
|
|
crypto = require('crypto');
|
|
var Parse = require('parse/node').Parse;
|
|
|
|
var OAuth = function (options) {
|
|
if (!options) {
|
|
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'No options passed to OAuth');
|
|
}
|
|
this.consumer_key = options.consumer_key;
|
|
this.consumer_secret = options.consumer_secret;
|
|
this.auth_token = options.auth_token;
|
|
this.auth_token_secret = options.auth_token_secret;
|
|
this.host = options.host;
|
|
this.oauth_params = options.oauth_params || {};
|
|
};
|
|
|
|
OAuth.prototype.send = function (method, path, params, body) {
|
|
var request = this.buildRequest(method, path, params, body);
|
|
// Encode the body properly, the current Parse Implementation don't do it properly
|
|
return new Promise(function (resolve, reject) {
|
|
var httpRequest = https
|
|
.request(request, function (res) {
|
|
var data = '';
|
|
res.on('data', function (chunk) {
|
|
data += chunk;
|
|
});
|
|
res.on('end', function () {
|
|
data = JSON.parse(data);
|
|
resolve(data);
|
|
});
|
|
})
|
|
.on('error', function () {
|
|
reject('Failed to make an OAuth request');
|
|
});
|
|
if (request.body) {
|
|
httpRequest.write(request.body);
|
|
}
|
|
httpRequest.end();
|
|
});
|
|
};
|
|
|
|
OAuth.prototype.buildRequest = function (method, path, params, body) {
|
|
if (path.indexOf('/') != 0) {
|
|
path = '/' + path;
|
|
}
|
|
if (params && Object.keys(params).length > 0) {
|
|
path += '?' + OAuth.buildParameterString(params);
|
|
}
|
|
|
|
var request = {
|
|
host: this.host,
|
|
path: path,
|
|
method: method.toUpperCase(),
|
|
};
|
|
|
|
var oauth_params = this.oauth_params || {};
|
|
oauth_params.oauth_consumer_key = this.consumer_key;
|
|
if (this.auth_token) {
|
|
oauth_params['oauth_token'] = this.auth_token;
|
|
}
|
|
|
|
request = OAuth.signRequest(request, oauth_params, this.consumer_secret, this.auth_token_secret);
|
|
|
|
if (body && Object.keys(body).length > 0) {
|
|
request.body = OAuth.buildParameterString(body);
|
|
}
|
|
return request;
|
|
};
|
|
|
|
OAuth.prototype.get = function (path, params) {
|
|
return this.send('GET', path, params);
|
|
};
|
|
|
|
OAuth.prototype.post = function (path, params, body) {
|
|
return this.send('POST', path, params, body);
|
|
};
|
|
|
|
/*
|
|
Proper string %escape encoding
|
|
*/
|
|
OAuth.encode = function (str) {
|
|
// discuss at: http://phpjs.org/functions/rawurlencode/
|
|
// original by: Brett Zamir (http://brett-zamir.me)
|
|
// input by: travc
|
|
// input by: Brett Zamir (http://brett-zamir.me)
|
|
// input by: Michael Grier
|
|
// input by: Ratheous
|
|
// bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
|
// bugfixed by: Brett Zamir (http://brett-zamir.me)
|
|
// bugfixed by: Joris
|
|
// reimplemented by: Brett Zamir (http://brett-zamir.me)
|
|
// reimplemented by: Brett Zamir (http://brett-zamir.me)
|
|
// note: This reflects PHP 5.3/6.0+ behavior
|
|
// note: Please be aware that this function expects to encode into UTF-8 encoded strings, as found on
|
|
// note: pages served as UTF-8
|
|
// example 1: rawurlencode('Kevin van Zonneveld!');
|
|
// returns 1: 'Kevin%20van%20Zonneveld%21'
|
|
// example 2: rawurlencode('http://kevin.vanzonneveld.net/');
|
|
// returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
|
|
// example 3: rawurlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
|
|
// returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'
|
|
|
|
str = (str + '').toString();
|
|
|
|
// Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
|
|
// PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
|
|
return encodeURIComponent(str)
|
|
.replace(/!/g, '%21')
|
|
.replace(/'/g, '%27')
|
|
.replace(/\(/g, '%28')
|
|
.replace(/\)/g, '%29')
|
|
.replace(/\*/g, '%2A');
|
|
};
|
|
|
|
OAuth.signatureMethod = 'HMAC-SHA1';
|
|
OAuth.version = '1.0';
|
|
|
|
/*
|
|
Generate a nonce
|
|
*/
|
|
OAuth.nonce = function () {
|
|
var text = '';
|
|
var possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
|
|
for (var i = 0; i < 30; i++) text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
|
|
return text;
|
|
};
|
|
|
|
OAuth.buildParameterString = function (obj) {
|
|
// Sort keys and encode values
|
|
if (obj) {
|
|
var keys = Object.keys(obj).sort();
|
|
|
|
// Map key=value, join them by &
|
|
return keys
|
|
.map(function (key) {
|
|
return key + '=' + OAuth.encode(obj[key]);
|
|
})
|
|
.join('&');
|
|
}
|
|
|
|
return '';
|
|
};
|
|
|
|
/*
|
|
Build the signature string from the object
|
|
*/
|
|
|
|
OAuth.buildSignatureString = function (method, url, parameters) {
|
|
return [method.toUpperCase(), OAuth.encode(url), OAuth.encode(parameters)].join('&');
|
|
};
|
|
|
|
/*
|
|
Retuns encoded HMAC-SHA1 from key and text
|
|
*/
|
|
OAuth.signature = function (text, key) {
|
|
crypto = require('crypto');
|
|
return OAuth.encode(crypto.createHmac('sha1', key).update(text).digest('base64'));
|
|
};
|
|
|
|
OAuth.signRequest = function (request, oauth_parameters, consumer_secret, auth_token_secret) {
|
|
oauth_parameters = oauth_parameters || {};
|
|
|
|
// Set default values
|
|
if (!oauth_parameters.oauth_nonce) {
|
|
oauth_parameters.oauth_nonce = OAuth.nonce();
|
|
}
|
|
if (!oauth_parameters.oauth_timestamp) {
|
|
oauth_parameters.oauth_timestamp = Math.floor(new Date().getTime() / 1000);
|
|
}
|
|
if (!oauth_parameters.oauth_signature_method) {
|
|
oauth_parameters.oauth_signature_method = OAuth.signatureMethod;
|
|
}
|
|
if (!oauth_parameters.oauth_version) {
|
|
oauth_parameters.oauth_version = OAuth.version;
|
|
}
|
|
|
|
if (!auth_token_secret) {
|
|
auth_token_secret = '';
|
|
}
|
|
// Force GET method if unset
|
|
if (!request.method) {
|
|
request.method = 'GET';
|
|
}
|
|
|
|
// Collect all the parameters in one signatureParameters object
|
|
var signatureParams = {};
|
|
var parametersToMerge = [request.params, request.body, oauth_parameters];
|
|
for (var i in parametersToMerge) {
|
|
var parameters = parametersToMerge[i];
|
|
for (var k in parameters) {
|
|
signatureParams[k] = parameters[k];
|
|
}
|
|
}
|
|
|
|
// Create a string based on the parameters
|
|
var parameterString = OAuth.buildParameterString(signatureParams);
|
|
|
|
// Build the signature string
|
|
var url = 'https://' + request.host + '' + request.path;
|
|
|
|
var signatureString = OAuth.buildSignatureString(request.method, url, parameterString);
|
|
// Hash the signature string
|
|
var signatureKey = [OAuth.encode(consumer_secret), OAuth.encode(auth_token_secret)].join('&');
|
|
|
|
var signature = OAuth.signature(signatureString, signatureKey);
|
|
|
|
// Set the signature in the params
|
|
oauth_parameters.oauth_signature = signature;
|
|
if (!request.headers) {
|
|
request.headers = {};
|
|
}
|
|
|
|
// Set the authorization header
|
|
var authHeader = Object.keys(oauth_parameters)
|
|
.sort()
|
|
.map(function (key) {
|
|
var value = oauth_parameters[key];
|
|
return key + '="' + value + '"';
|
|
})
|
|
.join(', ');
|
|
|
|
request.headers.Authorization = 'OAuth ' + authHeader;
|
|
|
|
// Set the content type header
|
|
request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
|
|
return request;
|
|
};
|
|
|
|
module.exports = OAuth;
|