In the prior post, I described the message flows required for granting a client application write access to a user’s Twitter status. In this post, I’ll cover the additional use case, posting a status message.
The output of a successful authorization of a client application is an oauth_token and an oauth_token_secret. Twitter does not “expire” these items and so the client can re-use them forever.
Typically, clients (applications) will store the oauth_token and oauth_token_secret in a settings file. A .NET application on Windows might put the file in %AppData%, in other words, \users\Someone\AppData\Roaming\Vendor\Appname\Settings.xml.
To post status updates to a user’s Twitter stream, the client application can simply POST to /1/statuses/update.xml or /1/statuses/update.json, including an Authorization header constructed as described previously. In fact there is just one request+response for this use case. This header is formed in exactly the same way as described in the first use-case, except that there are now different required parameters. For regular status updates, the client needs to construct the signature base using:
- all the oauth_ parameters (except the “secret” ones). This will include the oauth_token obtained from the approval dance, but not the oauth_verifier, which is a one-time thing, or the oauth_callback which is used only when requesting client approval.
- the complete status parameter, URL-encoded, which must be inserted into the base in observance of lexicographic sorting order.
Again, the client must normalize the parameters – sort by name, encode the values, and concatenate together with ampersands – and then sign the UTF-8 encoded payload. The added twist here is that the payload to be signed must include named query string parameters. In the case of a status update to Twitter, there is just one query string parameter, ‘status’. That parameter must be included in the signature base, with the value OAuth-percent-encoded as described previously. The result must be inserted into the base in observance of the lexicographic sorting requirement.
The resulting signature base looks something like this:
POST&http%3A%2F%2Fapi.twitter.com%2F1%2Fstatuses%2Fupdate.xml& oauth_consumer_key%3DFXJ0DIH50S7ZpXD5HXlalQ%26 oauth_nonce%3D1j91hk2m%26oauth_signature_method%3DHMAC-SHA1%26 oauth_timestamp%3D1336764858%26 oauth_token%3D59152613-HBwngBPTBUIrl7qKJXvBKaM0Ko1FpA2n5Hfmdh4uc%26 oauth_version%3D1.0%26 status%3DTwas%2520brillig%252C%2520and%2520the...
Notice that the actual status string is double-URL-encoded. Yes, you must URL-encode it once, and append it to the sorted list of parameters. Then that sorted list gets URL-encoded again, before being concatenated with the method and URL, separated by ampersands. The result, the signature base, is what you see here. To sign, the entire signature base is URL-encoded again, before being UTF-8 encoded and signed. This is the kind of thing that is hard to discern in the scattered Twitter documentation. A simple example like this makes everything clearer, though not exactly clear.
The resulting message with the computed signature looks like this:
POST http://api.twitter.com/1/statuses/update.xml?status=Twas%20bri.. HTTP/1.1 Authorization: OAuth oauth_consumer_key="xxxxxxxxxx", oauth_nonce="1j91hk2m", oauth_signature="YyyTkVQyecxqeGN%2BlgdhsZNkkJw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1336764858", oauth_token="59152613-PNjdWlAPngxOVa4dCWnLMIzZcUXKmwMoQpQWTkbvD", oauth_version="1.0" Host: api.twitter.com
(That Authorization header is all on one line). The response message, if you are getting XML, looks like this:
<status> <created_at>Fri May 11 19:34:12 +0000 2012</created_at> <id>201032478964711427</id> <text>Twas brillig, and the slithy toves...</text> <source>Dino's TweetIt</source> <truncated>false</truncated> <favorited>false</favorited> <in_reply_to_status_id></in_reply_to_status_id> <in_reply_to_user_id></in_reply_to_user_id> <in_reply_to_screen_name></in_reply_to_screen_name> <retweet_count>0</retweet_count> <retweeted>false</retweeted> <user> <id>59152613</id> <name>dino chiesa</name> <screen_name>dpchiesa</screen_name> <location>Seattle</location> <description></description> <url>http://dinochiesa.net</url> <protected>false</protected> <followers_count>0</followers_count> <profile_background_color>C0DEED</profile_background_color> <profile_text_color>333333</profile_text_color> <profile_link_color>0084B4</profile_link_color> <profile_sidebar_fill_color>DDEEF6</profile_sidebar_fill_color> <profile_sidebar_border_color>C0DEED</profile_sidebar_border_color> <friends_count>13</friends_count> <created_at>Wed Jul 22 15:20:26 +0000 2009</created_at> <favourites_count>0</favourites_count> <utc_offset>-28800</utc_offset> <time_zone>Pacific Time (US & Canada)</time_zone> <profile_background_tile>false</profile_background_tile> <profile_use_background_image>true</profile_use_background_image> <notifications>false</notifications> <geo_enabled>false</geo_enabled> <verified>false</verified> <following>false</following> <statuses_count>22</statuses_count> <lang>en</lang> <contributors_enabled>false</contributors_enabled> <follow_request_sent>false</follow_request_sent> <listed_count>0</listed_count> <show_all_inline_media>false</show_all_inline_media> <default_profile>true</default_profile> <default_profile_image>true</default_profile_image> <is_translator>false</is_translator> </user> <geo/> <coordinates/> <place/> <contributors/> </status>
If the client uses the .json suffix on the update URL, then it would get a JSON-formatted message containing the same information.
There may arise a situation in which the user has already approved the client application, but the client does not have access to the oauth_token and oauth_token_secret, and is therefore unable to properly construct the signature for the Authorization header. This might happen when a user gets a new PC or device, or tries to use the client application from a different PC, for example. In that case the client should simply request authorization again, as in Use Case #1, described in the previous post. If this happens, the /oauth/authenticate endpoint can automatically redirect back to your callback without requiring any user interaction or even showing any Twitter UI to the user.