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:
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 a 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);
}
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 class 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)
};</twitterrtpostresults>
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);
}</twitterrtpostresults>
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
.
async Task<TwitterRtPostResults> PostData(String url,
TwitterRtDictionary headerDictionary, TwitterRtDictionary requestDictionary = null)
{
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)
{
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”.