Making a Twitter OAuth API Call Using C#

Wednesday, May 23, 2012

I had an idea to create a Twitter management web application, to help me manage my Twitter account. I know there are Nuget packages(TweetSharp is the one I use), that would do this for me, but being the geek that I am, I just wanted to see how this all works for myself, and see if I could get my brain around what it actually takes to make an Oauth call.

Admittedly, if I needed to make a Twitter API call for a real project, I would just use TweetSharp. Thus, this post is more conceptual than practical in nature in that you probably would never need to do this, but its good to know how its done, all the same. 

OAuth Authorization

I previously posted how easy it was to make an API call to Goodreads, but doing the same thing for Twitter was a different story. Unlike Goodreads, Twitter requires to pass all the OAuth parameters in the HTTP header and the have to encrypted in a very specific manner. From the Twitter Development Site, you need to create an Authorization header and this header entry should look like this:

Authorization:

        OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog", 
              oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", 
              oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D", 
              oauth_signature_method="HMAC-SHA1", 
              oauth_timestamp="1318622958", 
              oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb", 
              oauth_version="1.0"

 

The Authorization Parameters

oauth_nonce: Twitter uses this parameter to determine if the same call has been sent multiple times; therefore, every time this call is made it needs to be unique. According to the specs, the value for this parameter needs to be a base64 encoding 32 bytes of random data. A timestamp in ticks works for this.

_oauthNonce = Convert.ToBase64String(new ASCIIEncoding().GetBytes(
                DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture)));

oauth_token: represents the user's permission to access their account with your application. For example a Twitter application will redirect you to a Twitter permission page and when you log in there, Twitter will redirect the user back to your app and pass back the token in the response. In my case, I am using my own token that I got from the Twitter development site.

oauth_version: This parameter should always be 1.0.

oauth_consumer_key: Tells Twitter which application is making the request. When you add an application to the Twitter development site, they give you this value.

oauth_signature_method: The signature method used by Twitter is HMAC-SHA1. 

oauth_timestamp: This method is the number of seconds from the UNIX epoch at the time the request was generated.

_timeSpan = DateTime.UtcNow - new DateTime(197011000);
_oathTimestamp = Convert.ToInt64(_timeSpan.TotalSeconds).ToString(CultureInfo.InvariantCulture);

The Signature (What the...?)

Twitter has a whole page dedicated to showing you how to create a signature, so I won't go into great detail, but there some things I want to elborate here. First off here is what the specs have to say:

 

These values need to be encoded into a single string which will be used later on. The process to build the string is very specific:
   1. Percent encode every key and value that will be signed.
   2. Sort the list of parameters alphabetically[1] by encoded key[2].
   3. For each key/value pair:
   4. Append the encoded key to the output string.
   5. Append the '=' character to the output string.
   6. Append the encoded value to the output string.
   7. If there are more key/value pairs remaining, append a '&' character to the output string.
[1] Note: The OAuth spec says to sort lexigraphically, which is the default alphabetical sort for many libraries.
[2] Note: In case of two parameters with the same encoded key, the OAuth spec says to continue sorting based on value. However, Twitter does not accept duplicate keys in API requests.

Here is the code for this process:

public string CreateSignature(string url)
        {
            //string builder will be used to append all the key value pairs
            var stringBuilder = new StringBuilder();
            stringBuilder.Append("POST&");
            stringBuilder.Append(Uri.EscapeDataString(url));
            stringBuilder.Append("&");
 
            //the key value pairs have to be sorted by encoded key
            var dictionary = new SortedDictionary<stringstring>
                                 {
                                     {"oauth_version", OathVersion},
                                     {"oauth_consumer_key", OauthConsumerKey},
                                     {"oauth_nonce", _oauthNonce},
                                     {"oauth_signature_method", OauthSignatureMethod},
                                     {"oauth_timestamp", _oathTimestamp},
                                     {"oauth_token", OauthToken}
                                 };
            
            foreach (var keyValuePair in dictionary)
            {
                //append a = between the key and the value and a & after the value
                stringBuilder.Append(Uri.EscapeDataString(string.Format("{0}={1}&", keyValuePair.Key, keyValuePair.Value)));
            }
            string signatureBaseString = stringBuilder.ToString().Substring(0, stringBuilder.Length - 3);
 
            //generation the signature key the hash will use
            string signatureKey =
                Uri.EscapeDataString(OauthConsumerKey) + "&" +
                Uri.EscapeDataString(OauthToken);
 
            var hmacsha1 = new HMACSHA1(
                new ASCIIEncoding().GetBytes(signatureKey));
 
            //hash the values
            string signatureString = Convert.ToBase64String(
                hmacsha1.ComputeHash(
                    new ASCIIEncoding().GetBytes(signatureBaseString)));
            
            return signatureString;
        }

Here are the corresponding steps for the corresponding tasks above.

  • For #1 I am using the URL.EscapeDataString to percent code every key value pair.
  • For #2 I am putting the key values in a sorted dictionary.
  • For #3, #4, #5, #6, and #7 I am iterating through each key value pair and encoding them and appending the "=" and "&" as needed.

From the MSDN site, here is the definition of what the Url.EscapeDataString does:

By default, the EscapeDataString method converts all characters except for RFC 2396 unreserved characters to their hexadecimal representation. If International Resource Identifiers (IRIs) or Internationalized Domain Name (IDN) parsing is enabled, the EscapeDataString method converts all characters, except for RFC 3986 unreserved characters, to their hexadecimal representation. All Unicode characters are converted to UTF-8 format before being escaped.

The string that generated from these steps should look something like this.

POST&http%3A%2F%2Fsearch.twitter.com%2Fsearch.json&oauth_consumer_key%3Dxxxxxxxxxxxxxxxxxxxxxx%26oauth_nonce%3DNjM0NzIzMTkzMzAzMzMyOTM0%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1336736930%26oauth_token%3Dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx%26oauth_version%3D1.0

Once the base key is generated, then you need to generate a signing key from your OAuth Consumer Key and Oauth Token. Note, that these entries are essentially your password to Twitter, so keep it hush, hush.

Finally, having the base signature and the signature key, I can then create a hash using HMACSHA1.

HMACSHA1 is a type of keyed hash algorithm that is constructed from the SHA1 hash function and used as an HMAC, or hash-based message authentication code. The HMAC process mixes a secret key with the message data, hashes the result with the hash function, mixes that hash value with the secret key again, then applies the hash function a second time. The output hash is 160 bits in length.

Building the Header

Once I have al my header values, including my signature, I can then create a header.

 

        public string CreateAuthorizationHeaderParameter(string signature, string timeStamp)
        {
            string authorizationHeaderParams = String.Empty;
            authorizationHeaderParams += "OAuth ";
            authorizationHeaderParams += "oauth_nonce=" + "\"" +
                                         Uri.EscapeDataString(OAuthNonce) + "\",";
 
            authorizationHeaderParams +=
                "oauth_signature_method=" + "\"" +
                Uri.EscapeDataString(OauthSignatureMethod) +
                "\",";
 
            authorizationHeaderParams += "oauth_timestamp=" + "\"" +
                                         Uri.EscapeDataString(timeStamp) + "\",";
 
            authorizationHeaderParams += "oauth_consumer_key="
                                         + "\"" + Uri.EscapeDataString(OauthConsumerKey) + "\",";
 
            authorizationHeaderParams += "oauth_token=" + "\"" +
                                         Uri.EscapeDataString(OauthToken) + "\",";
 
            authorizationHeaderParams += "oauth_signature=" + "\""
                                         + Uri.EscapeDataString(signature) + "\",";
 
            authorizationHeaderParams += "oauth_version=" + "\"" +
                                         Uri.EscapeDataString(OathVersion) + "\"";
            return authorizationHeaderParams;
        }

Once again, I need to percent escape each of the entries in the header and when the function is completed  the result should look something like this:

Authorization: OAuth oauth_nonce="NjM0NzIyNjg0NjAwMDI1MzMx",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1336686060",oauth_consumer_key="xxxxxxxxxxxxxxx",oauth_token="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",oauth_signature="xxxxxxxxxxxxxxxxxxxxxx",oauth_version="1.0"

Putting It All Together. Let's Search Twitter

Okay, now that I have a authorization, I can use it to make a call to Twitter. If I wanted to search on "programming" in the South Florida away I could build a URL like this (Note that in this particular type of call I don't actually need the header entry, but I would for more secure calls like viewing my direct messages or posting a new tweet):

http://search.twitter.com/search.json?q=programming&rpp=25&include_entities=true&result_type=recent&geocode=26.225947999999995,-80.18809300000001,100mi

In this case, I am passing the search term "programming", the longitude and latitude of where the search term originated from, and the max of twenty-fine matches. Here is the code that makes that call:

 

        public IList<SearchResponse> Search(SearchRequest search)
        {
            if (search == null || string.IsNullOrEmpty(search.Q)) return new List<SearchResponse>();

            string query = _httpServer.UrlEncode(search.Q);
            
            string url = "http://search.twitter.com/search.json";
            string fullUrl = string.Format("{0}?q={1}&rpp=25&include_entities=true", url, query);
 
            if (!string.IsNullOrEmpty(search.ResultType))
            {
                fullUrl = string.Format("{0}&result_type={1}", fullUrl, search.ResultType);
            }
 
            if (search.GeoInfoCode != null && !string.IsNullOrEmpty(search.GeoInfoCode.Latitude))
            {
                fullUrl = string.Format("{0}&geocode={1}", fullUrl, search.GeoInfoCode.Code);
            }
 
            string signatureString = _oAuthCreationService.CreateSignature(url);
            string authorizationHeaderParams = _oAuthCreationService.CreateAuthorizationHeaderParameter(
                signatureString, _oAuthCreationService.OAuthTimeStamp);
 
            const string method = "GET";
            HttpWebRequest request = _httpRequestResponse.GetRequest(fullUrl, authorizationHeaderParams, method);
            var responseText = _httpRequestResponse.GetResponse(request);
            var searchResponse = _mapSearch.Map(responseText);
            return searchResponse;
        }

You can see my HttpWebRequest and HttpWebResponse code from my Goodreads API blog post. The response is returned in JSON, which if I wanted, I could return right back to the client.

Conclusion

Well, I hope this helps in understanding OAuth a little bit more. Again, if you want make calls to the Twitter API and do want to go through all this yourself, then get yourself a package off of Nuget like TweetSharp. It's easy to integrate into your application, and the project site has some pretty good documentation.

You can see the full code for this API on Github.

References & Resources

comments powered by Disqus