Results 1 to 14 of 14
  1.    #1  
    Hi Everyone,

    I have a simple game for WebOS I developed called EvaDots. I want to add the ability for users to be able to post their scores on Twitter. I've tried doing this already using the "share" url, but it does not work 80% of the time, so I'm trying to go the API route instead. I've done some research, and I believe xAuth is best choice for what I'm trying to do. I've already emailed Twitter support and got my app approved to use xAuth.

    After some more research, I found some code in the Palm Dev forums for using Xauth. I've adapted this technique and I've got it ALMOST working but I'm totally stuck and I'm in need of some help.

    I've imported the jsSHA library to sign the twitter requests. I can use a twitter username/password, sign the request, and get a valid response from "https://api.twitter.com/oauth/access_token" with an oAuth token and an oAuth secret. Where it fails is when I actually try to update the status with the score. I keep getting the error from twitter saying "Invalid signature". I've spent HOURS on this and I can't figure it out.

    I don't think it is a problem with signing the base string, since I use the same method to get the access tokens and it works fine.

    Here is my code, if anyone could take a look and give me some hints, I would be VERY happy:

    Code:
      var updateUrl = "http://api.twitter.com/1/statuses/update.json";
          	     var nonce = this.getNonce();
          	     Mojo.Log.info(response.responseText);
    	     
          	      //get the oauth data
          	      // probably a better way to do this but oh well
          	      var o = response.responseText.split("&");
          	      var t = o[0].split("=");
          	      var o_auth_token=t[1];
          	      Mojo.Log.info("o auth token: "+o_auth_token);
          	      t = o[1].split("=");
          	      var o_auth_secret=t[1];
          	      Mojo.Log.info("o auth secret: "+o_auth_secret);
          	  
                  var status = "I just scored "+this.scoreForT+" points on EvaDots for WebOS! How well can you do?";
                  var postBody = encodeURIComponent("status="+status);
                  var timestamp = Math.floor((new Date()).getTime()/1000);
                  var update_data=
                      "oauth_consumer_key=" + encodeURIComponent(constants.consumerKey) +
                       "&oauth_nonce=" + nonce + 
                       "&oauth_signature_method=HMAC-SHA1" + 
                       "&oauth_timestamp=" + timestamp + 
                        "&oauth_token="+ o_auth_token +
                        "&oauth_version=1.0";
                 var base_string = "POST&" + encodeURIComponent(updateUrl) + "&" + encodeURIComponent(update_data);     
                 Mojo.Log.info("BASE STRING: "+ base_string);
    	     
    	     //Sign the base string
                 var shaObj = new jsSHA(base_string, "ASCII");
                 var oauth_signature = shaObj.getHMAC(constants.consumerSecret+"&"+o_auth_secret, "ASCII", "B64");
                 Mojo.Log.info("oas: "+oauth_signature);
                 oauth_signature = encodeURIComponent(oauth_signature+"=");  
                
    	     //set auth header
                 var auth_header = 'OAuth realm="", oauth_consumer_key="'+constants.consumerKey + 
                                   '",oauth_nonce="'+nonce+'",oauth_signature="'+oauth_signature+
                                   '",oauth_signature_method="HMAC-SHA1",oauth_timestamp="'+ timestamp +
                                   '",oauth_token="'+o_auth_token +'",oauth_version="1.0"';
                 
                 Mojo.Log.info("HEADER: "+auth_header);
                 
    	     //send request	    
                 var req = new Ajax.Request(updateUrl, {
                    method: 'POST',
                    requestHeaders: ["Authorization", auth_header],
                    postBody: postBody,                   
                    onSuccess:  function(response) {
                    	Mojo.Log.info("update tweet resp:"+response.responseText);
                    },
                    onFailure: function(response) {
                    	Mojo.Log.info("failed to post to twitter: "+response.responseText);
                  }
          	     })
    I even checked the base string using an online tool (before signing) and it said it was valid. I am totally stuck.
  2. #2  
    I just added Twitter to my app so I know how much of a pain oAuth is. I've done it before with Digg, so I figured it would be pretty easy, but there's always something that gets you.

    One of the things that got me with Twitter was the fact that they expect the timestamp to be in UTC time, which is not documented on their site. I noticed you're using the getTime call which does not return a UTC time. This is probably one of your problems.

    I'm not sure what else may be wrong since I went down the regular oAuth path (vs. xAuth).
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  3.    #3  
    Quote Originally Posted by DanPLC View Post
    I just added Twitter to my app so I know how much of a pain oAuth is. I've done it before with Digg, so I figured it would be pretty easy, but there's always something that gets you.

    One of the things that got me with Twitter was the fact that they expect the timestamp to be in UTC time, which is not documented on their site. I noticed you're using the getTime call which does not return a UTC time. This is probably one of your problems.

    I'm not sure what else may be wrong since I went down the regular oAuth path (vs. xAuth).
    Thanks for the reply! I'm not certain that it is a problem with the timestamp... according to the Twitter docs:
    "oauth_timestamp - an integer representing the number of seconds that have passed since the unix epoch" and that is what getTime returns, I believe. Plus I use that to get the oAuth tokens and it works.

    And as far as xAuth or vs oAuth, from where I am stuck xauth and oauth are the same. I already have my oauth secret and oauth token, I should be able to use them to sign my base string and make things happen with the API.

    I think the problem might be my nonce. How did you generate your nonce value? And what library/method did you use to sign your base string?
  4. #4  
    Quote Originally Posted by TheDrizzle View Post
    Thanks for the reply! I'm not certain that it is a problem with the timestamp... according to the Twitter docs:
    "oauth_timestamp - an integer representing the number of seconds that have passed since the unix epoch" and that is what getTime returns, I believe. Plus I use that to get the oAuth tokens and it works.

    And as far as xAuth or vs oAuth, from where I am stuck xauth and oauth are the same. I already have my oauth secret and oauth token, I should be able to use them to sign my base string and make things happen with the API.

    I think the problem might be my nonce. How did you generate your nonce value? And what library/method did you use to sign your base string?
    I must have missed that in the docs, because "unix epoch" is UTC time. If you're calling "getTime", it will never work. I found multiple topics on this subject on the web. You'll get an "invalid signature" error if your time does not match their time. And your time has to be in UTC.

    As far as I can tell, a nonce is simply a random value with a certain number of characters. I'm using a library called oAuth.jsjsjs $that$ $I$ $believe$ $is$ $found$ $on$ $Twitter$'$s$ $site$ ($as$ $well$ $as$ $many$ $others$). $It$ $has$ $a$ $lot$ $of$ $built$-$in$ $functions$ $including$ $one$ $to$ $generate$ $a$ $nonce$ $value$. $However$ $their$ $timestamp$ $function$ $uses$ $getTime$, $so$ $I$ $had$ $to$ $modify$ $it$ $to$ $generate$ $the$ $UTC$ $time$ $for$ $it$ $to$ $work$.

    Packaged with oAuth.jsjsjs $is$ $sha$.$js$ $which$ $oAuth$.$js$ $uses$ $to$ $do$ $all$ $of$ $the$ $SHA$ $stuff$.
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  5.    #5  
    Hmm, I see. After some more searching, I'm going to try this "inelegant" workaround when I get a chance later today.

    Change:
    Code:
      
    var timestamp = Math.floor((new Date()).getTime()/1000);
    To:
    Code:
    var dt  = new Date();
    var timestamp = Math.floor( (new Date(dt.toUTCString() )).getTime()/1000);
    According to the w3schools site, this should work:
    JavaScript Date Object

    I don't have access to my WebOS development tools at the moment, so I'll give this a try this evening.

    Thanks for the advice.
  6. #6  
    I don't have my code in front me right now, but I think I did something like this:

    Code:
    var currentTime = new Date();
    var currentUTCTimeInSecs = Math.floor(Date.parse(currentTime.toUTCString()) / 1000);
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  7.    #7  
    Well, the timestamp UTC thing did not help. I did find one problem, I'm supposed to include the status in the Base String when updating status.
    So the base string building code becomes:
    Code:
    var update_data=
                      "oauth_consumer_key=" + encodeURIComponent(constants.consumerKey) +
                       "&oauth_nonce=" + nonce + 
                       "&oauth_signature_method=HMAC-SHA1" + 
                       "&oauth_timestamp=" + timestamp + 
                        "&oauth_token="+ o_auth_token +
                        "&oauth_version=1.0"+
                        "&status="+encodeURIComponent(status);
     var base_string = "POST&" + encodeURIComponent(updateUrl) + "&" + encodeURIComponent(update_data);
    However, that did not fix it either, still get "Incorrect signature". I found another site that validates your signature string ( here ) and what I was returning did not match. So I changed the sha1.jsjsjs $to$ $the$ $one$ $that$ $is$ $used$ $in$ $the$ $oauth$.$js$ $library$. $Now$ $I$ $can$ $get$ $the$ $signature$ $that$ $is$ $returned$ $from$ $my$ $app$ $to$ $match$ $that$ $site$. $But$ $it$ $it$ $STILL$ $not$ $working$ .

    I know it is not a signature problem, so something is wrong with either how I am building the base string or the authorization header.

    If anyone has any more advice, I welcome it.
  8. #8  
    I know you don't believe it yet, but the timestamp was one of your problems. Once you have it working, change back to non-UTC time and you'll see it'll fail. Also in regards to the timestamp, make sure the timestamp on your device (or emulator) matches the UTC time returned by Twitter in its response. If they're off by too much, the call will fail.

    Another potential problem I see is that you're calling encodeURIComponent twice for some of the components of your base string. You call it once when setting "update_data". Then you call it again when setting "base_string".

    Have you printed out your base_string in the log to see if it looks ok?
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  9.    #9  
    From what I read in the twitter dev docs, you're supposed to URL encode each parameter of of the base string, AND THEN before you send the signature with the authorization header, you are supposed to URL encode the signature itself that was returned from the sha function.

    I think maybe when I'm building the update data string itself, I should not be URL encoding there, but I've tried removing it and I still get the same error.

    Here is an example of a recent base string I generated:

    Code:
    POST&http%3A%2F%2Fapi.twitter.com% 
    2F1%2Fstatuses%2Fupdate.json&oauth_consumer_key 
    %3DJxPeA0aTWPfkULuWu80dyA%26oauth 
    _nonce%3DIpx2fKgwUXlQ18d%26oauth_signature_method%3DHMAC- 
    SHA1%26oauth_timestamp% 
    3D1291099840%26oauth_token%3D186684223- 
    buwCSVt0NJQ7BDUo0q5OZo4jWjgSCDhPT2IBEGRF% 
    26oauth_version%3D1.0%26status%3Dwow
    If you plug that into this tool:
    OAuth signature checker
    It says that it is syntactically correct.

    I wonder if I'm parsing the response from my xauth call wrong and the oauth secret is not right when I'm signing the base string.
  10. #10  
    You might be correct. I'm using the oAuth library which does all that for you. So I didn't really have to worry about it.
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  11. #11  
    @DanPLC:
    Which oAUth lib are you using? There are at least 2 or 3 I've found and one of them failed to get a token.
    Maybe you can provide a link?
  12. #12  
    Quote Originally Posted by rretsiem View Post
    @DanPLC:
    Which oAUth lib are you using? There are at least 2 or 3 I've found and one of them failed to get a token.
    Maybe you can provide a link?
    It's been awhile since I originally found it when I worked on my Web Hopper app, but I'm pretty sure it's this one: oauth - Revision 1242: /code/javascript

    Although like I mentioned, I had to modify the routine that calculates the timestamp so that it uses UTC time.
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami
  13.    #13  
    Well, after hours of toil I FINALLY got it working last night.

    I've changed so many things, I'm not entirely sure what I did to fix it, but here is what I think my problems were:
    1. I was not including status=<whatever> as part of the base string.
    2. I was URL encoding the POST Body, which I should not have been doing
    3. When I was having all these problems, I thought it may have to do with special characters in the status string. So, I hard coded a simple status ("wow") into my base string. HOWEVER, my post body still had the longer status "I just scored blah blah", so the status in my base string did not match the post body.

    Problem three was what I changed last night that finally got it to work. That was a dumb move on my part, I should have realized I needed to change the status string in both places.

    Thanks again Dan for all of your help and suggestions!
  14. #14  
    Glad you got it working!
    Quick Post: The quick way to post messages and photos to Twitter & Facebook (video link)
    Music Player (Remix): The next generation music listening experience on webOS (video link)
    GeoStrings: Set location-based reminders and never forget another task (video link)

    Twitter: @Hedami

Tags for this Thread

Posting Permissions