Authorizing Twitter API Calls in Javascript
I have been thinking of playing around Twitter APIs since a long time, but never sat down patiently to get it done. Procrastination is at its highest 😥 Anyways, I did it finally, withTwick - an Angular app to fetch and display Twitter data.
Getting Authorization header
Twitter has a good documentation onhow to authorize a request.
Lets take an example request:
GET https://api.twitter.com/1.1/users/show.json?screen_name=void_imagineerAuthorization: OAuthoauth_consumer_key="XXxxXXxxXXXXXxxx",oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1318622958",oauth_token="ddddddd-xxxXXxxxxXXxxXXXXXxxxxxXXXXxxxxXXxxxxXX",oauth_version="1.0"
OAuth Fields | value |
---|---|
oauth_consumer_key | Get fromapps.twitter.com |
oauth_nonce | Any unique random string (eg: base64 encoding of consumerKey & timestamp) |
oauth_signature | Described in next section |
oauth_signature_method | HMAC-SHA1 |
oauth_timestamp | unix timestamp |
oauth_token | Get fromapps.twitter.com |
oauth_version | 1.0 |
Twitter access keys are stored inkeys.json
only for local testing.
TWITTER_CONSUMER_KEY= XXxxXXxxXXXXXxxx,TWITTER_CONSUMER_SECRET= xxxXXxxxxXXxxXXXXXxxxxxXXXXxxxxXXxxxxXX,TWITTER_ACCESS_TOKEN= ddddddd-xxxXXxxxxXXxxXXXXXxxxxxXXXXxxxxXXxxxxXX,TWITTER_ACCESS_TOKEN_SECRET= XXXxxXXxxXXXXXxxxxxXXXXxxxxXXxxxxXX;
functiongetAuthorization(httpMethod,baseUrl,reqParams){// Get acces keysletkeysJson=require('keys.json');constconsumerKey=keysJson.TWITTER_CONSUMER_KEY,consumerSecret=keysJson.TWITTER_CONSUMER_SECRET,accessToken=keysJson.TWITTER_ACCESS_TOKEN,accessTokenSecret=keysJson.TWITTER_ACCESS_TOKEN_SECRET;// timestamp as unix epochlettimestamp=Math.round(Date.now()/1000);// nonce as base64 encoded unique random stringletnonce=btoa(consumerKey+':'+timestamp);// generate signature from base string & signing keyletbaseString=oAuthBaseString(httpMethod,baseUrl,reqParams,consumerKey,accessToken,timestamp,nonce);letsigningKey=oAuthSigningKey(consumerSecret,accessTokenSecret);letsignature=oAuthSignature(baseString,signingKey);// return interpolated stringreturn'OAuth '+'oauth_consumer_key="'+consumerKey+'", '+'oauth_nonce="'+nonce+'", '+'oauth_signature="'+signature+'", '+'oauth_signature_method="HMAC-SHA1", '+'oauth_timestamp="'+timestamp+'", '+'oauth_token="'+accessToken+'", '+'oauth_version="1.0"';}
This function can be used to generateAuthorization header. Example below shows sampleAPI call using Angular $resource
$resource(url,null,{get:{method:'GET',headers:{'Authorization':getAuthorization('GET','https://api.twitter.com/1.1/users/show.json',{'screen_name':'void_imagineer'})}},options).get({'screen_name':'void_imagineer'}).$promise
Generating Signature
OAuth 1.0 signature is created by HMAC-SHA1 encryption, where base string and signing key are generated as follows:
1. Base string
functionoAuthBaseString(method,url,params,key,token,timestamp,nonce){returnmethod+'&'+percentEncode(url)+'&'+percentEncode(genSortedParamStr(params,key,token,timestamp,nonce));};
2. Signing key
functionoAuthSigningKey(consumer_secret,token_secret){returnconsumer_secret+'&'+token_secret;};
3. Signature
functionoAuthSignature(base_string,signing_key){varsignature=hmac_sha1(base_string,signing_key);returnpercentEncode(signature);};
Supporting functions
// Percent encodingfunctionpercentEncode(str){returnencodeURIComponent(str).replace(/[!*()']/g,(character)=>{return'%'+character.charCodeAt(0).toString(16);});};
// HMAC-SHA1 Encoding, uses jsSHA libvarjsSHA=require('jssha');functionhmac_sha1(string,secret){letshaObj=newjsSHA("SHA-1","TEXT");shaObj.setHMACKey(secret,"TEXT");shaObj.update(string);lethmac=shaObj.getHMAC("B64");returnhmac;};
// Merge two objectsfunctionmergeObjs(obj1,obj2){for(varattrinobj2){obj1[attr]=obj2[attr];}returnobj1;};
// Generate Sorted Parameter String for base string paramsfunctiongenSortedParamStr(params,key,token,timestamp,nonce){// Merge oauth params & request params to single objectletparamObj=mergeObjs({oauth_consumer_key:key,oauth_nonce:nonce,oauth_signature_method:'HMAC-SHA1',oauth_timestamp:timestamp,oauth_token:token,oauth_version:'1.0'},params);// Sort alphabeticallyletparamObjKeys=Object.keys(paramObj);letlen=paramObjKeys.length;paramObjKeys.sort();// Interpolate to string with format as key1=val1&key2=val2&...letparamStr=paramObjKeys[0]+'='+paramObj[paramObjKeys[0]];for(vari=1;i<len;i++){paramStr+='&'+paramObjKeys[i]+'='+percentEncode(decodeURIComponent(paramObj[paramObjKeys[i]]));}returnparamStr;};
Pitfalls for client-only app:
My idea was toimplement this app entirely on frontend. This isnot feasible because of 2 reasons:
Can’t ensure security for access keys: Storing these keys without revealing to browser console is impossible.
Hard fix: make the user to connect their own access keys by enabling Twitter authentication.Browser blocking CORS: Twitter API response doesn’t have
Access-Control-Allow-Origin
header set, because of that browsesrs will not accept responses.
Quick fix: tunnel requests though acorsproxy server.
You can check out the code for Twick (Angular Twitter data fetch app) in Github.
Twick Code at Github