Ads API - UnAuthorized Access 401 - Only works with Twitter Generated URL


#1

I have been trying to access Twitter’s Ads API and kept getting the following error:

{"errors":[{"code":"UNAUTHORIZED_ACCESS","message":"This request is not properly authenticated"}],"request":{"params":{}}}

for URL: https://ads-api.twitter.com/0/accounts/:ACCOUNTID/campaigns?sort_by=start_time-desc
with Authorization Header:

OAuth oauth_consumer_key="<Consumer Key (API Key)>", oauth_nonce="bb829f56bec74c9fbb161a198743ccb3", oauth_signature="<generated oauth signature>", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1430413665", oauth_token="<Access Token>", oauth_version="1.0"

My app has Read-Write permission and has Consumer Key (API Key), Consumer Secret (API Secret), Access Token, Access Token Secret.

If I use the OAuth Signature Generator ( after logging in) to generate the complete URL with Authorization header in https://dev.twitter.com/ads/reference/get/accounts/%3Aaccount_id/campaigns , it works , Twitter’s OAuth generated URL is like below

curl --get 'https://ads-api.twitter.com/0/accounts/<accountId>/campaigns'
 --data 'sort_by=start_time-desc' --header 'Authorization: OAuth 
oauth_consumer_key="<consumer-key>", 
oauth_nonce="c313492cbabf83c8d535bf35f6fba068", 
oauth_signature="<oauth-signature>", 
oauth_signature_method="HMAC-SHA1", oauth_timestamp="1430414438", 
oauth_token="<access-token>", 
oauth_version="1.0"' --verbose

It only FAILS when I generate the signature as described here: https://dev.twitter.com/oauth/overview/authorizing-requests

I have tests to verify the signature generated by Twitter OAuth is same as mine. Given the only factors which are changing are “oauth_nonce” which is a randomly generated alphanumeric and the “oauth_timestamp”. If I use twitter generated “ouath_nonce” and “oauth_timestamp”, my code generate the same “oauth_signature”.

I cannot make out why twitter generated ones return HTTP 200 while the authorization header generated by my code fails with HTTP 401.

I checked timestamp, they are very accurate, so I can rule out timestamp. I compared my authorization header to twitter oauth generated header, white spaces, comma, everything seem same.

Twitter generated OAuth header works, mine doesn’t although tests reveal oauth_signature generated by code is correct. Please note I am only making 1 request to fetch list of campaigns. This is not 2 legged or 3 legged approach.

I am only including Authorization header to https://ads-api.twitter.com/0/accounts/:account_id/campaigns to fetch list of campaigns. If I use the URL generated in my code, it works.

Please advise if I am missing something. Does twitter OAuth OAuth Generator do something else while generating OAuth Signature, something like whitelisting the generated OAuth Signature on the backend?

Your help is greatly appreciated!

Many Thanks,
Nirmal


#2

(moving to the Ads API category for attention)


#3

Hi Nirmal:

It might help if you could provide side-by-side comparisons the the curl command created by the OAuth Signature Generator that works and the curl command you constructed yourself that fails (or are you trying to authenticate via a different mechanism)?

You say you “checked timestamp”, but what do you mean by “very accurate”?

Because the timestamp is part of what’s included to generate the signature value itself, there’s no way that the same signature could work for two different timestamps.


#4

Hi Arthur,

Thanks for the reply. What I meant by the timestamp being accurate is, my local machine time is similar to that returned by twitter server and so my local machine time is not offset by minutes or so.

I know the signature generation depends on timestamp, oauth_nonce which is randomly generated unique id and few others. When I generate a curl command using twitter’s OAuth Signature generator, I can see the timestamp & oauth_nonce and I used in my local test as input to verify the signature generated by my code, they match.

My code always generates new uuid and picks up timestamp from local system to create the url, here are the 2 urls created by twitter and my code which were generated few seconds apart.

I am not using any other form of authentication /authorization. This is the only call made:

For security reasons, i cannot paste ACCOUNTID, CONSUMER-KEY AND OAUTH-TOKEN, but the rest are here, if you need further info, please let me know your email address, we can take it over there:

Generated by my code (Returns HTTP/1.1 401):

curl --get 'https://ads-api.twitter.com/0/accounts/:ACCOUNTID/campaigns' --data 'sort_by=start_time-desc' --header 'Authorization: OAuth oauth_consumer_key=":CONSUMER-KEY", oauth_nonce="afc6203ffb4c417f97ee28beaa4f329c", oauth_signature="jhSlfsztZV1zpaIgSO6cjr7rXI0%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1430847154", oauth_token=":OAUTH-TOKEN", oauth_version="1.0"' --verbose

Twitter Generated Curl (works and returns HTTP/1.1 200):

curl --get 'https://ads-api.twitter.com/0/accounts/:ACCOUNTID/campaigns' --data 'sort_by=start_time-desc' --header 'Authorization: OAuth oauth_consumer_key=":CONSUMER-KEY", oauth_nonce="438b6af827994fd622be34fa3159950e", oauth_signature="kbK2gNzovo0ieqB4EYWiB9xJ9pA%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1430847148", oauth_token=":OAUTH-TOKEN", oauth_version="1.0"' --verbose

Many Thanks,
Nirmal


#5

Found the problem, its a coding error on my end. While generating oAuthSignature as described here https://dev.twitter.com/oauth/overview/creating-signatures , I did the following mistakes:

  1. While generating ParameterString, I used timestamp in millis instead of seconds
  2. Generated random uuid here (this wasn’t a problem until i started building Authorization Header)

Signature base string and subsequently signature was generated from this parameter string.

  1. While creating oAuth Authorization header, where we have to specify timestamp and nonce, i thought i was using the same timestamp and nonce (random uuid) used to generate oAuth signature but it wasn’t the case. I called the helper method which generates new random uuid and fetches new timestamp.

So the oAuth signature was generated using different random uuid (nonce) and timestamp compared to what was on oAuth Header causing HTTP/401

Because the problem was in the data used to generate parameter string, all my tests focussed on given the set of parameters a valid oauth signature is returned which is working fine led to the confusion why twitter generated url works and mine not.

After fixing these data issues, its working.


Unauthorized_access
#6