.nettwitteroauthtwitpic

using TwitPic + OAuth to upload a photo + tweet to Twitter (.NET C#) - why no tweet?


I'm uploading photos to TwitPic, using OAuth, from a .NET app written in C#.

The oAuth stuff is a little tricky. I found two bits of .NET code to handle it, but wasn't satisfied with either. DotNetOpenAuth seemed to be pretty heavy, more than I need. (Just wanna do oAuth signatures and token requests). The OAuthBase.cs code seemed confused and inelegant to me. I had to pass 6 string parameters to methods, and if I got any out of order, woe be unto me.

So I wrote some code to do it myself, it's fairly small and it seems to work. It works to acquire "request tokens". It works to pop the authorize web page and to acquire "access tokens". It also works to upload photos to TwitPic.

All the HTTP responses come back as 200 or 201.

The upload HTTP message looks like this:

POST http://api.twitpic.com/2/upload.json HTTP/1.1
Content-Type: multipart/form-data; boundary=48cb9a6d-1f1d-432d-b6e3-307e32e8228a
X-Auth-Service-Provider: https://api.twitter.com/1/account/verify_credentials.json
X-Verify-Credentials-Authorization: OAuth realm="http://api.twitter.com/",
  oauth_consumer_key="Dv1er93yKzEMn74hZfPmJA",
  oauth_nonce="51fi305k",
  oauth_signature="4oWcmZcd%2F%2F81JslJ70xFXFm8%2BQs%3D",
  oauth_signature_method="HMAC-SHA1",
  oauth_timestamp="1292277715",
  oauth_token="59152613-z8EP4GoYS1Mgo3E29JfIqBnyTRlruAJs8Bkvs3q0T",
  oauth_version="1.0"
Host: api.twitpic.com
Content-Length: 14605
Expect: 100-continue
Connection: Keep-Alive

--48cb9a6d-1f1d-432d-b6e3-307e32e8228a
Content-Disposition: file; name="media"; filename="CropperCapture[10].jpg"
Content-Type: image/jpeg
....
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a
Content-Disposition: form-data; name="key"

twitpic-api-key-here
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a
Content-Disposition: form-data; name="message"

uploaded from Yappy. (at 12/13/2010 5:01:55 PM)
--48cb9a6d-1f1d-432d-b6e3-307e32e8228a--

The json I get back from the upload is like this:

{"id":"3f0jeiw5",
 "text":"uploaded from Yappy. (at 12\/13\/2010 5:01:55 PM)",
 "url":"http:\\/twitpic.com\/3f0jeiw5",
 "width":257,
 "height":184,
 "size":14156,
 "type":"jpg",
 "timestamp":"Mon, 13 Dec 2010 22:02:06 +0000",
 "user":{
   "id":54591561,"screen_name":"bfavre"}
}

But the problem is, after I upload the image to Twitpic, the image is available on TwitPic, but the associated message never appears on Twitter.

What gives?

I read in a random blog post that using TwitPic+oAuth requires me to post the tweet message in a separate HTTP transaction, direct to Twitter. huh? I thought the mail purpose of oAuth was to allow consumers to do things on my behalf - like allowing TwitPic to post a tweet for me.

Any hints?


EDIT
I'm learning a little more here. This blog post from May 2010 suggests to me that using a value for X-Auth-Service-Provider of https://api.twitter.com/1/account/verify_credentials.json tells TwitPic to call "verify_credentials.json" on twitter.com when it gets my request. If it really IS just verifying my credentials, this would explain why no Tweet is posted.

The post also suggests that swapping that out and replacing it with https://api.twitter.com/1/status/update.json should allow me to update Twitter via TwitPic with delegation. But it is a forward-looking post - it says that getting this capability requires work on Twitter's part.

I haven't found an example HTTP message that does this yet. Anyone?


UPDATE
After converting the verify URL to https://api.twitter.com/1/status/update.json and using POST for the signature, I get a 401 response code:

{"errors":
  [{"code":401,
    "message":"Could not authenticate you (header rejected by twitter)."}]
}

This is basically the same problem as described here, in the twitter dev forum. The suggestion at the end of that thread was that the signature-computation algorithm is wrong, but I think that's incorrect, as my sig algorithm works with all other requests.


Solution

  • I don't know the final answer, but I think that the changes that were described as coming "Real Soon Now" in Raffi's blog post of May 2010, have not actually been made, for whatever reason.

    So in fact, using OAuth, you do have to post separately to TwitPic and to Twitter.

    It's not hard to do this in Twitter, though. Just do a POST on the statuses/update.xml URL.

    private void Tweet(string message)
    {
        var twitterUpdateUrlBase = "http://api.twitter.com/1/statuses/update.xml?status=";
        var url = twitterUpdateUrlBase + UrlEncode(message);
    
        var authzHeader = oauth.GenerateAuthzHeader(url, "POST");
    
        var request = (HttpWebRequest)WebRequest.Create(url);
        request.Method = "POST";
        request.PreAuthenticate = true;
        request.AllowWriteStreamBuffering = true;
        request.Headers.Add("Authorization", authzHeader);
    
        using (var response = (HttpWebResponse)request.GetResponse())
        {
            if (response.StatusCode != HttpStatusCode.OK)
            MessageBox.Show("There's been a problem trying to tweet:" +
                            Environment.NewLine +
                            response.StatusDescription +
                            Environment.NewLine +
                            Environment.NewLine +
                            "You will have to tweet manually." +
                            Environment.NewLine);
        }
    }
    

    There are two tricky parts to that code: First is the UrlEncode() call. OAuth specifies that the urlencoding must use uppercase letters. The built-in .NET routine uses lowercase. So be sure to upcase.

    The second tricky part is is getting the OAuth Authorization Header. If you use an OAuth library package it should be pretty simple. For those who want a simple one, you can get one here: OAuth.cs. (Get the OAuthManager downloaD)