This post describes the inner workings of TwitterRT and how it fulfills the Twitter OAUTH API. You do not need to read this if you are just using TwitterRT and do not really care how it works.
Other posts in this series:
- Problems with Metro's WebAuthentication Sample
- Posting to Twitter from Metro in C#
- Adding Twitter to the AppBar
- Adding a Twitter Authorization Charm
- TwitterRT – The Design
There are three major parts to issuing a tweet on behalf of your user:
- Register your application with Twitter.
This is a bit involved and not explained in this post.
Instead, first read Posting To Twitter from Metro in C#. - Signing in to Twitter from your application.
This is more complicated and is described in 3 steps below. - Posting Tweets.
This is pretty easy and described at the end of this post.
Signing in to Twitter
The authoritative description of what needs to be done to sign in to Twitter is very well described in Twitter’s very own Implementing Sign in with Twitter. Their document outlines three steps. It would be best if you kept both their document and this post open and side-by-side.
The days of asking your user for their Twitter name and password are over. Life on the web does not work that way anymore. Your application will never know the user’s password. So go ahead and delete that XAML you created that prompts the user for that information. It is useless to you.
We are now in the world of OAUTH. That means you go through some complicated process to get permission to interact with Twitter on the user’s behalf. If the user changes their Twitter password, you don’t care because your application keeps on working just fine. However, the user can revoke your applications permission at any time using the Twitter website.
The title of this section is “Signing in to Twitter”. That is a bit misleading. Your application only needs to “Sign in” once, not every session. When you perform the one-time sign in, you get a special access token. You will save that token on the user’s computer. Then, when your application runs another day, you can use that token to Tweet again. The token never expires.
So basically, we are saying, “Hey Twitter, I want to authorize my application to post on behalf of whoever is running my application, but I don’t know their user name or password (and I don’t care).”
Again, there are three steps to obtaining the token:
Step 1: Obtaining a request token
Step 1 is letting Twitter know we want to start the process of signing in the user and obtaining a token with which we can tweet.
async Task<TwitterRtPostResults> Step1()
{
var header = new TwitterRtDictionary();
header.Add("oauth_callback", Uri.EscapeDataString(_callbackUrl));
header.Add("oauth_consumer_key", _consumerKey);
header.Add("oauth_nonce", GenerateNonce());
header.Add("oauth_signature_method", _signatureMethod);
header.Add("oauth_timestamp", GenerateSinceEpoch());
header.Add("oauth_version", _oauthVersion);
return await PostData(_requestTokenUrl, header); // should contain oauth_token, oauth_token_secret, and oauth_callback_confirmed
}
In TwitterRT, we do this by first creating a TwitterRtDictionary. The dictionary is simply a SortedDictionary<String, String> with a few special functions that really reduce the complexity of the rest of the code. Then we build a header in accordance with what Twitter requires. The callback URL and the consumer key are provided to the constructor of TwitterRT (and described in Posting To Twitter from Metro in C#). The other parameters are pretty trivial.
The PostData function is the most tricky part of the TwitterRT library, and it will be described later. For now, we just need to know that PostData will navigate to https://api.twitter.com/oauth/request_token, and send along a header that contains a bunch of parameters. PostData then gathers up the response from Twitter and places it in this handy class called TwitterRtPostResults.
{
public enum EStatus
{
Success = 0,
Canceled = 1,
Error = 2,
}
public EStatus Status { get; set; }
public String Description { get; set; }
public TwitterRtDictionary Dictionary { get; set; }
}
What we are specifically looking for in the response is a parameter called “oauth_token” (in the Dictionary). No, this is not the token that allows us to tweet yet. We are a long way away from having that, but we are ready for step 2.
Step 2: Redirecting the user
Step 2 is the step where we pop up a Twitter webpage that asks the user to log in with their own user name and password. They also have to indicate that they will allow our application to tweet on their behalf. When this webpage is open, our application “looks the other way” while the user types in their password.
Metro has a very nice function that opens up a pop-up webpage. It is called AuthenticateAsync. All we pass it is the token we got from step 1. We do not have to use AuthenticateAsync, but could open our own popup. This is so much nicer though.
async Task<TwitterRtPostResults> Step2(String oauthToken)
{
try
{
var url = _authorizeUrl + "?oauth_token=" + oauthToken;
var war = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, new Uri(url), new Uri(_callbackUrl));
switch (war.ResponseStatus)
{
case WebAuthenticationStatus.Success:
return new TwitterRtPostResults
{
Status = TwitterRtPostResults.EStatus.Success,
Dictionary = new TwitterRtDictionary(war.ResponseData) // should contain oauth_token and oauth_verifier
};
What we are specifically looking for in the response are the parameters called “oauth_token” and “oauth_verifier”. No, these are not the tokens that allows us to tweet yet, but we are getting closer. We are ready for step 3.
Step 3: Converting the request token to an access token
Step 3 is where we actually get the token we wanted in the first place. We request this token by passing the two parameters we obtained from step 2. Notice that this time when we call PostData, we are passing a lot of parameters in the header, and one parameter in the request of the HTTP Post.
async Task<TwitterRtPostResults> Step3(String oauthToken, String oauthVerifier)
{
var header = new TwitterRtDictionary();
header.Add("oauth_consumer_key", _consumerKey);
header.Add("oauth_nonce", GenerateNonce());
header.Add("oauth_signature_method", _signatureMethod);
header.Add("oauth_timestamp", GenerateSinceEpoch());
header.Add("oauth_token", oauthToken);
header.Add("oauth_version", _oauthVersion);
var request = new TwitterRtDictionary();
request.Add("oauth_verifier", Uri.EscapeDataString(oauthVerifier));
return await PostData(_accessTokenUrl, header, request); // should contain oauth_token, oauth_token_secret, user_id, and screen_name
}
What we are specifically looking for in the response are the parameters called “oauth_token” and “oauth_token_secret”. Yes, these are the tokens that allows us to tweet!
We also get to finally find out the user_id and screen_name of our user. However, we don’t need to know these to tweet. We can hold on to them for the purposes of performing queries and other Twitter actions.
To some extent, that is all there is to signing in to Twitter.
Some Gory Details
Now for a brief description of the intricacies of PostData.
{
// See https://dev.twitter.com/docs/auth/creating-signature
var combinedDictionaries = new TwitterRtDictionary(headerDictionary);
combinedDictionaries.Add(requestDictionary);
var signatureBase = "POST&" + Uri.EscapeDataString(url) + "&" + Uri.EscapeDataString(combinedDictionaries.ToStringA());
var keyMaterial = CryptographicBuffer.ConvertStringToBinary(_consumerSecret + "&" + OauthTokenSecret, BinaryStringEncoding.Utf8);
var algorithm = MacAlgorithmProvider.OpenAlgorithm("HMAC_SHA1");
var key = algorithm.CreateKey(keyMaterial);
var dataToBeSigned = CryptographicBuffer.ConvertStringToBinary(signatureBase, BinaryStringEncoding.Utf8);
var signatureBuffer = CryptographicEngine.Sign(key, dataToBeSigned);
var signature = CryptographicBuffer.EncodeToBase64String(signatureBuffer);
var headers = "OAuth " + headerDictionary.ToStringQ() + ", oauth_signature=\"" + Uri.EscapeDataString(signature) + "\"";
return await PostData(url, headers, (requestDictionary == null) ? String.Empty : requestDictionary.ToString());
}
Each time we send data to Twitter, we need to sign the content of the transmission (so no one can spoof us).
The data that we sign includes the text “POST”, plus the Twitter URL we are accessing, plus all the header and request data. All of these items need to be separated by an ampersand. The header and request data needs to be combined and then sorted. That is why we create a combined dictionary.
We sign the data with a signature that consists of our consumer secret data (provided to the constructor of TwitterRT) and the oauth_token_secret separated by an ampersand. However, in both steps 1 and 3 we do not yet have an oauth_token_secret. Remember that we get the oauth_token_secret as a return value from step 3. So until we have an oauth_token_secret, we need to sign all our requests with a signature that consists of our consumer secret data plus an ampersand (and nothing else). Yeah, I would think the ampersand should not be there if oath_token is missing, but I suppose that is the result of some tradition.
Once we have the signature we have a bit more housekeeping to do before forwarding the post. Specifically we need to send the header as comma-space separated parameters where all the values have to be within double-quotes. So why do we sign the header as a bunch of ampersand connected parameters with no double-quotes? Tradition again I suspect. We also need to add an additional parameter that contains the signature, called “oath_signature”. Of course, this signature needs to have special characters escaped.
In fact, there are many parameters that need to have special characters escaped, and many that do not. Either someone used a dart board to determine when to escape special characters and when not to or it is the result of some tradition. In any case, these are the fields that need to be escaped (all others should not be escaped):
- status (when tweeting)
- oauth_callback
- oauth_verifier
- all signed data
- signature
Now you would think that the request parameters (like the header parameters) need to be comma-space separated parameters where all the values have to be within double-quotes too. Well, not quite. They need to be comma-space separated parameters where all the values are not within double-quotes. I suspect tradition is behind this one too.
There is another function called PostData that actually performs the HttpWebRequest. That function is pretty boring and self-explanatory.
Posting Tweets
The authoritative description of what needs to be done to tweet is very well described in Twitter’s very own Authorizing a request. Their document outlines all the parameters. It would be best if you kept both their document and this post open and side-by-side.
public async Task<Boolean> UpdateStatus(String status) // a.k.a. tweet
{
IsTweeting = true;
Status = "Tweeting";
var header = new TwitterRtDictionary();
header.Add("oauth_consumer_key", _consumerKey);
header.Add("oauth_nonce", GenerateNonce());
header.Add("oauth_signature_method", _signatureMethod);
header.Add("oauth_timestamp", GenerateSinceEpoch());
header.Add("oauth_token", OauthToken);
header.Add("oauth_version", _oauthVersion);
var request = new TwitterRtDictionary();
request.Add("status", Uri.EscapeDataString(status));
var response = await PostData(_updateStatusUrl, header, request);
IsTweeting = false;
if (response.Status == TwitterRtPostResults.EStatus.Success)
{
Status = status;
return true;
}
else
{
Status = response.Description;
return false;
}
}
The only parameter that needs special mention is the oauth_token. But of course we know that already came from step 3 above. Then all we need to do is load up the request with the status parameter, and poof, we are done. Of course we again rely on PostData to do all the heavy lifting here.
I hope this has been helpful on providing some insight into the whole OAUTH process. I think OAUTH is a great concept, very powerful in its implementation, but full of very annoying “traditions”.
Hi, thank you for the sample and thanks for the thorough documentation. It's clean and easy to use. I'm trying to port it to Windows Phone 8. Right away, I noticed these assemblies are not available:
ReplyDeleteusing Windows.Security.Authentication.Web;
using Windows.Security.Cryptography;
using Windows.Security.Cryptography.Core;
Since WebAuthenticationBroker isn't supported in Windows Phone 8, is there an alternate to getting the response data? I can use Windows.System.Launcher that can launch the URI. But again, I don't know how to get the data back. Or is this a job for the WebBrowser control? I don't know enough about these classes to make a wise decision. If you can provide some advice that'll be greatly appreciated. Thank you
Miyudreams,
DeleteBefore the WebAuthenticationBroker was available I was successful in launching the URI. When launching the URI, the user is provided the token in a web page. The token is short enough to make it reasonable for the user to type it in to your application. Still, I doubt you can display an input region from your application at the same time the browser is visible, so the user will have to memorize the code or write it down on paper. Not the best solution for a phone.
At this point, adapting TwitterRT for the phone is not a priority for me. The source is available here: http://twitterrt.codeplex.com/. If you or anyone else would like to contribute, please issue a codeplex request and I will be happy to accept. Or, copy and paste and create your own project.
Maybe someone else with WP8 experience will chime in here?
- John
Hi, this is a great sample, nicely done. How do I get the user's information using TwitterRT? I tried using https://api.twitter.com/1.1/users/show.json?screen_name={screen name from TwitterRt} but it returns a bad request. How do I do it using TwitterRT? Thanks!
ReplyDeleteTake a look at step 3 above... the last line reads:
Deletereturn await PostData(_accessTokenUrl, header, request); // should contain oauth_token, oauth_token_secret, user_id, and screen_name
}
I think you need screen_name... I hope this helps.
Would PostData() return all the user's information like profile picture? I am using your TwitterRT:
DeleteTwitterRt twRT = new TwitterRt(twitterClientID, twitterClientSecret, twitterCallbackUrl);
await twRT.GainAccessToTwitter();
then I got the screen_name and UserID by twRt.UserID and twRt.ScreenName.
I need to get the Profile Picture of the user, is that possible using your TwitterRT? Thanks!
Paulo,
DeleteI have never done this with TwitterRT. Sorry! I do not have time just now to dive into this.
It's alright, may I know your algorithm for GenerateNonce() and GenerateSinceEpoch()? Thanks so much! I really appreciate your help.
DeleteTwitterRt source resides here: http://twitterrt.codeplex.com/
DeleteThanks so much! :)
DeleteHi
DeleteI tried to use WebAuthenticationBroker.AuthenticateAsync from a windows app store application to be authorized in twitter and I have come across the following problem:
The authorization twitter page appears but after the authorization I am getting a page with a pin number and no way to return back to application unless using back arrow that will return me UserCancel result. How can I retrieve the token in the code in this case? Could you please help me?
Many thanks in advance!
Victor.
Victor,
DeleteLet me know if you ran the demo Windows Store App I supplied at: http://twitterrt.codeplex.com/
Does the demo app work for you?
Unfortunately the demo gives me 401 Unauthorized error.
DeleteI suppose the WebAuthenticationBroker adds something like mobile or desktop agent in the request header and twitter gives me pin instead of returning token. In this case I will have to write my own popup with WebView control.
DeleteVictor,
DeleteYou need to first authorize your application as described here: http://w8isms.blogspot.com/2012/05/posting-to-twitter-from-metro-in-c.html
If you mean steps 2 and 3, I have done them and request_token is working fine from my code. I need to implement the second step to get token but i do not see the way how to get the token using WebAuthenticationBroker because it never returns.
Delete