Friday, June 2, 2017

Twitter REST API: Bad Authentication data

Leave a Comment

I'm trying to post a tweet but for any reason it doesn't work as expected.

I suspect that the issue could be related to the signature string, but following what twitter says according to signing requests looks ok.

Here is my code:

function postTweet(user_id, AccessToken, AccessTokenSecret) {   var base_url = 'https://api.twitter.com/1.1/statuses/update.json',     oauth_nonce = randomString(),     oauth_signature,     oauth_timestamp = Math.floor(new Date().getTime() / 1000),     reqArray,     req,     signature_base_string,     signing_key;    reqArray = [     "include_entities=true",     'oauth_consumer_key="' + CONFIG.TWITTER_CONSUMER_KEY + '"',     'oauth_nonce="' + oauth_nonce + '"',     'oauth_signature_method="HMAC-SHA1"',     'oauth_timestamp="' + oauth_timestamp + '"',     'oauth_token="' + AccessToken + '"',     'oauth_version="1.0"',     'status=' + encodeURIComponent("hello world")   ];    req = reqArray.sort().join('&');    signature_base_string = "POST&" + encodeURIComponent(base_url) + "&" + encodeURIComponent(req);    signing_key = CONFIG.TWITTER_CONSUMER_KEY_SECRET + '&' + AccessTokenSecret;    oauth_signature = encodeURIComponent(CryptoJS.HmacSHA1(signature_base_string, signing_key).toString(CryptoJS.enc.Base64));    return $http.post('https://api.twitter.com/1.1/statuses/update.json', {     status: 'hello world'   }).then(function (response) {     return response;   }).catch(function (error) {     console.log(error);   }); } 

enter image description here

As a response, I get that:

enter image description here

UPDATE

considering that in my project I already have $cordovaOauthUtility I started using it this way:

function postTweet(accessToken, accessTokenSecret) {   var params, signature;    params = {     include_entities: true,     oauth_consumer_key: CONFIG.TWITTER_CONSUMER_KEY,     oauth_nonce: $cordovaOauthUtility.createNonce(10),     oauth_signature_method: "HMAC-SHA1",     oauth_token: accessToken,     oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),     oauth_version: "1.0"   };    signature = $cordovaOauthUtility.createSignature('POST', 'https://api.twitter.com/1.1/statuses/update.json', params, { status: "hello" }, CONFIG.TWITTER_CONSUMER_KEY_SECRET, accessTokenSecret);    return $http.post('https://api.twitter.com/1.1/statuses/update.json', {     status: "hello"   }, {     headers: {       Authorization: signature.authorization_header     }   })     .then(function (response) {       return response;     }).catch(function (error) {       console.log(error);     }); } 

enter image description here

UPDATE 2

After trying all the posibilities, the problem persist. Here I paste a plnkr where I have my code.

3 Answers

Answers 1

You are using crypto's HmacSHA256 but sending HMAC-SHA1 as the oauth_signature_method parameter which is the twitter one.

You should probably change your code to

oauth_signature = CryptoJS.HmacSHA1(signature_base_string, signing_key).toString(CryptoJS.enc.Base64); 

If you look at your authorization header, you can also notice that something is wrong with it. Indeed, you can see that the oauth_nonce and the oauth_version are prefixed by a & sign, which shouldn't be the case and most likely mean to the api you are not specifying them. It probably comes from the fact you are using the same reqArray to construct both the signature and the header, or your code is not updated.

You also probably don't want to change the global headers sent from your app, in case another request is sent to another api at the same time. Rather, you should send this authorization header only for this specific xhr.

return $http.post('https://api.twitter.com/1.1/statuses/update.json', {   status: 'hello world', }, {   headers: {     Authorization: auth,   }, }) 

Answers 2

Well, you're clearly adding oauth_token in your request array but it didn't show up in the screenshot? Is the AccessToken in the params undefined?

EDIT

According to the documentation, we must append double quotes to the headers. Try this?

reqArray = [     "include_entities=true",     'oauth_consumer_key="'+CONFIG.TWITTER_CONSUMER_KEY+'"',     'oauth_nonce="'+oauth_nonce+'"',     'oauth_signature_method="HMAC-SHA1"',     'oauth_timestamp="'+oauth_timestamp+'"',     'oauth_token="'+AccessToken+'"',     'oauth_version="1.0"',     'status='+encodeURIComponent("hello world")   ]; 

Answers 3

Yikes.

I've downloaded your plnkr bundle and added a read only application key set. I only had to set up and make one change to get a {"request":"\/1.1\/statuses\/update.json","error":"Read-only application cannot POST."} response. Initially I was receiving {"errors":[{"code":32,"message":"Could not authenticate you."}]}.

Remove status: "hello" from between the curly brackets { } where you create your signature.

signature = $cordovaOauthUtility.createSignature('POST', 'https://api.twitter.com/1.1/statuses/update.json', params, { }, twitter.consumer_secret, twitter.access_token_secret); 

My request headers become the following:

:authority:api.twitter.com :method:POST :path:/1.1/statuses/update.json :scheme:https accept:application/json, text/plain, */* accept-encoding:gzip, deflate, br accept-language:en-US,en;q=0.8 authorization:OAuth     oauth_consumer_key="x",oauth_nonce="QFMmqiasFs",oauth_signature_method="HMAC-SHA1",oauth_token="y",oauth_timestamp="1496340853",oauth_version="1.0",oauth_signature="7Ts91LKcP%2FrYsLcF5WtryCvZQFU%3D" content-length:18 content-type:application/json;charset=UTF-8 origin:http://localhost referer:http://localhost/twits/ user-agent:Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36 

Googling eventually led me to a tutorial: Displaying the Twitter Feed within Your Ionic App. I noted his general createTwitterSignature function does not take parameters and tweaked your code similarly.

function createTwitterSignature(method, url) {     var token = angular.fromJson(getStoredToken());     var oauthObject = {         oauth_consumer_key: clientId,         oauth_nonce: $cordovaOauthUtility.createNonce(10),         oauth_signature_method: "HMAC-SHA1",         oauth_token: token.oauth_token,         oauth_timestamp: Math.round((new Date()).getTime() / 1000.0),         oauth_version: "1.0"     };     var signatureObj = $cordovaOauthUtility.createSignature(method, url, oauthObject, {}, clientSecret, token.oauth_token_secret);     $http.defaults.headers.common.Authorization = signatureObj.authorization_header; } 

I've read conflicting things about why there should/shouldn't be other parameters there, but I believe the signature is just supposed to be the basis of access and doesn't hash in every operation you want to perform - see Understanding Request Signing For Oauth 1.0a Providers.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment