Error:32 Could not authenticate you - using Wininet


#1

Hi,

Have bumped into this error message whilst updating my code to use the v1.1 API. To be fair, I think it was only working properly before with the v1.0 API by fluke, now things have been tightened up…

I’m using libauth to generate the authentication data, the actual requests are made using Wininet calls - HttpOpenRequest etc. As it stands, GET requests work fine (I can retrieve my timelines) but POST requests always come back with error:32.

I am 99% certain that the oauth data is correct because what I can do consistently is take the Authorization line which my code generates, along with the URL encoded POST data and paste it into a curl command line (using the same layout as Twitter’s OAUTH tool) and this works. So my guess is this is something to do with how the Wininet calls handle the data. Of course, this being SSL I can’t even sniff the packets to have a look but what I have been doing is using the HTTP interface to try and spot any differences. A typical curl request looks like this:

Hypertext Transfer Protocol
POST /1.1/statuses/update.json HTTP/1.1\r\n
User-Agent: curl/7.21.0 (i686-pc-linux-gnu) libcurl/7.16.4 OpenSSL/0.9.6i zlib/1.1.4\r\n
Host: api.twitter.com\r\n
Accept: /\r\n
[truncated] Authorization: OAuth oauth_consumer_key=“xxxxxxxxxxxxxxxx”, oauth_nonce=“iIOrLma9uJVIghY”, oauth_signature=“2JD9%2FgbmppfsGjBZmVQd2fucqk0%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1371302904”, oauth_token="
Content-Length: 32\r\n
Content-Type: application/x-www-form-urlencoded\r\n
\r\n
[Full request URI: http://api.twitter.com/1.1/statuses/update.json]
Line-based text data: application/x-www-form-urlencoded
status=somtext

I’ve made mine look as close to this as possible - notably adding in the Content-Type and Accept fields to the header & checked them on Wireshark. Has made zip all difference.

Here is the request from my code:

POST http://api.twitter.com/1.1/statuses/update.json HTTP/1.1\r\n
Accept: */*\r\n
[truncated] Authorization: OAuth oauth_consumer_key=xxxxxxxxxxxxxxxxx, oauth_nonce=iIOrLma9uJVIghY, oauth_signature=ohDzc6ULvbOsfVkXP8pxrU8izlU%3D, oauth_signature_method=HMAC-SHA1, oauth_timestamp=1371409596, oauth_token=
Content-Type: application/x-www-form-urlencoded\r\n
User-Agent: OAS Playout Twitter TrackLister (Mozilla)\r\n
Host: api.twitter.com\r\n
Content-Length: 58\r\n
Pragma: no-cache\r\n
Cookie: lang=en; guest_id=v1%3A130738749735889324\r\n
\r\n
[Full request URI: http://api.twitter.comhttp://api.twitter.com/1.1/statuses/update.json]

Line-based text data: application/x-www-form-urlencoded
status=sometext

Here is the relevant code chunk:

hInetConnect = InternetConnect ( hInetOpen, TwitterHostName, // host (api.twitter.com) INTERNET_DEFAULT_HTTPS_PORT, // port NULL, // username NULL, // passwd INTERNET_SERVICE_HTTP, // want http 0, // no options dContext ) ; // ?
  if ( hInetConnect )
  {
    // issue the document request
    hHttpRequest = HttpOpenRequest ( hInetConnect,
                                     "POST",                // verb = 'POST'
                                     pUrl,                  // document name - https://api.twitter.com/1.1/statuses/update.json
                                     NULL,                  // version = 'HTTP/1.1'
                                     NULL,                  // referrer = none
                                     AcceptTypes,           // "*/*"
                                     INTERNET_FLAG_SECURE |
                                     INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_NO_AUTH | INTERNET_FLAG_RELOAD,
                                     dContext ) ;           // ?

    if ( hHttpRequest )
    {
     // OauthHeader holds the "Authorization: oauth_consume_key=..."
      strcat ( OauthHeader, "Content-Type: application/x-www-form-urlencoded\r\n" ) ;
      HttpAddRequestHeaders ( hHttpRequest, OauthHeader, strlen(OauthHeader), 0 ) ;
     
      if ( HttpSendRequest ( hHttpRequest,
                             NULL,           // optional headers - none
                             0,              // opt header length
                             TwitterBuf,       // URL encoded status text
                             strlen(TwitterBuf) ) )           // opt data len
      {

Any help appreciated as I’ve just about run out of things to try here.

Rgs,

Jon.


#2

Ok, think I’ve got something working now. I have in fact gone back to my original code which simply embedded the oauth signatures et al into the POST request rather than attempting to (re)create them in the request header. It is after all what the oauth library passes back to me. So the key thing(s) I think I had to do differently over and above what I had before:

  • make sure that the URL you are signing has https rather than http in it.
  • add the following to the request header:

Content-Type: application/x-www-form-urlencoded\r\n

This last point I think was the crucial thing I needed to get it to work.

I didn’t have to re-order the oauth tokens alphabetically.

It’s also worth noting that HTTP requests still currently work, for me this was invaluable in comparing the traffic between a curl request and one I was producing.


#3

Thanks for posting how you resolved this, Jon.


#4

Very helpful thank you! application/x-www-form-urlencoded is what was missing in my case.