Skip to content

Instantly share code, notes, and snippets.

@aleclarson
Last active August 29, 2015 13:56
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aleclarson/8892714 to your computer and use it in GitHub Desktop.
Save aleclarson/8892714 to your computer and use it in GitHub Desktop.
OAuth Signing an HTTP request to Twitter's REST API v1.1 via Parse Cloud Code
<!DOCTYPE html>
<html>
<head></head>
<body>
<script type="text/javascript" src="https://cdn.jsdelivr.net/parse/1.2.9/parse.min.js"></script>
<script>
Parse.initialize('parse app id', 'parse app key');
Parse.Cloud.run('userFriends')
.always(function (res) {
console.log(JSON.parse(res.message));
});
</script>
</body>
</html>
var twitter = require('cloud/twitter.js');
define({
'userFriends': userFriends
});
// So I can define all my Parse Cloud Code functions in a single object.
function define (object) {
for (var key in object) {
Parse.Cloud.define(key, object[key]);
}
};
function userFriends (req, res) {
twitter.request({
user: req.user,
url: 'friends/ids',
params: {
user_id: req.user.get('twitterID'),
count: 1
}
}).then(res.success, res.error);
}
var
CONSUMER_KEY = 'your consumer key',
CONSUMER_SECRET = 'your consumer secret',
ACCESS_TOKEN = 'your application access token',
TOKEN_SECRET = 'your application token secret',
nonceAlphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
ENCODING_METHOD = to_rfc3986,
crypto = require('crypto');
exports.request = function (opts) {
var request = {};
if (!opts.url) return console.error('request(opts): opts.url missing');
request.url = 'https://api.twitter.com/1.1/' + opts.url + '.json';
request.method = opts.method || 'GET';
request.params = opts.params || {};
if (!opts.user) return console.error('request(opts): opts.user missing');
var authProps = getAuthProperties(opts.user, request);
request.headers = {
'Authorization': getAuthHeader(authProps),
'User-Agent': USER_AGENT
};
return Parse.Cloud.httpRequest(request);
};
function getAuthHeader (authProps) {
var oauth = mapObject(authProps, commaSpaceEncoding).sort();
oauth.push(oauth.pop().replace(', ', ''));
oauth.unshift('OAuth ');
return oauth.join('');
};
function getAuthProperties (user, request) {
var props = {};
props.oauth_consumer_key = CONSUMER_KEY;
props.oauth_nonce = getNonce();
props.oauth_signature_method = 'HMAC-SHA1';
props.oauth_timestamp = Math.floor(new Date() / 1000).toString();
props.oauth_token = user.get('twitterToken');
props.oauth_version = '1.0';
props.oauth_signature = getAuthSignature(user, request, props);
return props;
};
function getAuthSignature (user, request, oauth) {
var signingKey, body, signatureBase;
signingKey =
ENCODING_METHOD(CONSUMER_SECRET) + '&' + ENCODING_METHOD(user.get('twitterTokenSecret'));
body =
mapObject(oauth, ampersandEncoding)
.concat(mapObject(request.params, ampersandEncoding))
.sort();
// Remove trailing ampersand
body.push(body.pop().replace('&', ''));
signatureBase =
request.method.toUpperCase() + '&' +
ENCODING_METHOD(request.url) + '&' +
ENCODING_METHOD(body.join(''));
return crypto.createHmac('sha1', signingKey)
.update(signatureBase)
.digest('base64');
};
function getNonce () {
var text = [];
for(var i = 0; i < 32; i++) {
text.push(nonceAlphabet.charAt(Math.floor(Math.random() * nonceAlphabet.length)));
}
return text.join('');
};
function mapObject (object, factory) {
var array = [];
for (var name in object) {
array.push(factory(object[name], name));
}
return array;
};
// Used with mapObject() to create percent-encoded strings like this: key1=val1&key2=val2&key3=val3
function ampersandEncoding (val, key) {
return ENCODING_METHOD(key) + '=' + ENCODING_METHOD(val) + '&';
};
// Used with mapObject() to create percent-encoded strings like this: key1="val1", key2="val2", key3="val3"
function commaSpaceEncoding (val, key) {
return ENCODING_METHOD(key) + '="' + ENCODING_METHOD(val) + '", ';
};
// From: http://af-design.com/blog/2008/03/14/rfc-3986-compliant-uri-encoding-in-javascript/
function to_rfc3986 (string) {
return encodeURIComponent(string).replace('!','%21').replace('*','%2A').replace('(','%28').replace(')','%29').replace("'",'%27');
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment