Last active
August 29, 2015 13:56
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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