Twitter Development: The OAuth Specification

This article is excerpted from Chapter 6: Basic Authentication and OAuth of the Wrox book, Professional Twitter Development: With Examples in .NET 3.5, by Daniel Crenna, and is reused by permission of the publisher. This may not be reused without publisher permission.

Twitter Development:  The OAuth Specification

 

The OAuth specification is thorough, detailed, and does not compromise. Although each of the component parts that make up the OAuth process are themselves straightforward, it is easy to miss subtle details, which often lead to complete failure to authenticate your applications. In this section, you learn how to implement the specific aspects of the specification that apply to you. You can read the OAuth v1.0 specification in detail at http://oauth.net.
 

OAuth Consumers and Publishers
The segments of the OAuth specification that apply to you cover consumers and providers. In your scenario, your application is the consumer of OAuth authentication services, and Twitter is the publisher. Your role as a consumer is to provide authentication credentials based on a unique consumer token and secret (similar to a username and password) that identify your application to Twitter. You obtain this initial set of credentials when you configure your application on the Twitter development page.
Encoding
OAuth parameters expect URL encoding. The encoding must be according to the URI specification and not application/x-www-form-urlencoded, which means that spaces must encode as %20 and not +, and all hexadecimal representations must appear in uppercase, i.e., %2F and not %2f. Because of this, you should use Uri.EscapeDataString, rather than HttpUtility.UrlEncode.
Generating Timestamps
Every OAuth request must specify the time it was generated explicitly. This is useful to compare the declared time of the request and the time it is received by the publisher; it helps to rule out tampering if the request is received at an unreasonable distance from the time it was reported. OAuth declares timestamps in Unix Epoch time, or the number of seconds elapsed since January 1, 1970. To create this timestamp, you can use the following example which converts a DateTime instance representing the current date and time into a TimeSpan object using implicit conversion.
System;
            
namespace OAuthLibrary
{
    public static class OAuth
    {
        public static long CreateTimestamp()
        {
            var now = DateTime.UtcNow;
            var then = new DateTime(1970, 1, 1);
            
            var timespan = (now – then);
            var timestamp = (long)timespan.TotalSeconds;
            
            return timestamp;
        }
    }
}
You might notice that the DateTime instance is created by accessing the UtcNow property rather than Now. This is because all OAuth servers operate under the assumption that the time provided is in universal time, rather than the local time of the machine making the request. This is important because your OAuth call might silently fail if the timestamp is far enough away from universal coordinated time that it appears as if it has expired.
Generating Nonces
A nonce is a randomly generated alphabetical string that increases the security of hashing algorithms by introducing noise, or entropy, in an otherwise linear process. Nonces accompany timestamps as an extra security measure; because a nonce is created for every web request, it is helpful to think of it as a unique request identifier similar to a database ID, that helps distinguish multiple calls with an otherwise identical signature. This is useful to prevent replay attacks and other forms of hacking, and is akin to a one-time password. The following code demonstrates creating a random 12-character nonce.
using System.Text;
            
public static class OAuth
{
    private const string ALPHANUMERIC =
    “abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”;
            
    public static string CreateNonce()
    {
        var sb = new StringBuilder();
        var random = new Random();
            
        for(var i = 0; i <= 12; i++)
        {
            var index = random.Next(ALPHANUMERIC.Length);
            sb.Append(ALPHANUMERIC[index]);
        }
             
        return sb.ToString();
    }
}
Generating Signatures
With the timestamp and nonce created, you need to learn how to deliver OAuth information to Twitter in a secure way. This is accomplished by creating a secure signature and passing additional parameters with your web requests. Signature generation is the most error prone and sensitive aspect of OAuth; make one mistake, and the signature created will never match the required parameters, causing authentication to fail. A signature is an cryptographically hashed string representing the entire body of the OAuth authentication message, which means it is impossible to decrypt but its value is still verifiable. Table 6-1 provides all the parameters that are used to create a signature.
Table 6-1: OAuth Signature Components
OAuth Parameter
Description
oauth_consumer_key
This is the public key identifying your application. It is provided by Twitter when you configure your OAuth settings.
oauth_consumer_secret
This is the secret key identifying your application, provided by Twitter when you configure your OAuth settings. This private key is used to create the signature but is never shared when sending requests.
oauth_timestamp
This is the Unix epoch timestamp you generated earlier.
oauth_nonce
This is the nonce string value you generated earlier.
oauth_version
This is the version of the OAuth specification in action; today, Twitter uses the final v1.0 specification, so this value is typically “1.0.”
oauth_token
This is the public token used for tracking user authorization. You will learn more about token use in the next section.
oauth_token_secret
This is the secret token used for tracking user authorization. This private token is used to create the signature but is never shared when sending requests.
oauth_signature_method
The chosen method of security used to sign OAuth requests. This can be PLAINTEXT, HMAC-SHA1, or RSA-SHA1. Currently, Twitter supports only the HMAC-SHA1 algorithm.
 
Generating a signature involves collecting standard information about the outgoing web request, and using this information as a safeguard by including it with the signature along with the OAuth parameters included previously. This collection of information is normalized as a single string value, and is known as the signature base. It consists of the following components:
§ The HTTP method of the outgoing request in all capital letters, followed by a non-encoded & character, followed by
§ A normalized representation of the target URL, followed by a non-encoded & character, followed by
§ A concatenated list of all web request parameter pairs, including non-OAuth parameters, sorted by name and then by value.
A normalized URL is one that does not contain query or fragment data, or standard port declarations (port 80 for HTTP and port 443 for HTTPS); it is also presented in lowercase. The following code illustrates how to build a utility method to normalize a URL.
using System;
            
public static class OAuth
{
    public static string NormalizeUrl(string url)
    {
        Uri uri;
            
        // only work with a valid URL in lowercase
        if(Uri.TryCreate(url, UriKind.Absolute, out uri))
        {
            // only include non-standard ports
            string port = “”;
            if(uri.Scheme.Equals(“http”) && uri.Port != 80 ||
               uri.Scheme.Equals(“https”) && uri.Port != 443 ||
               uri.Scheme.Equals(“ftp”) && uri.Port != 20)
            {
                port = “:” + uri.Port;
            }
            
            // use only the scheme, host, port, and path
            url = uri.Scheme + “://” + uri.Host + port + uri.AbsolutePath;
        }
            
        return url;
    }
}
Normalizing the parameters of a request involves an initial collection of all pre-existing request parameters, adding all public, known OAuth parameters, and sorting the resulting collection by name and then value. After this process is complete, the results are concatenated as a single string of name value pairs. To perform this task, you can use the code in the following listing.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
            
public static class OAuth
{
    public static string NormalizeRequestParameters
            (NameValueCollection parameters)
    {
        var sb = new StringBuilder();
            
        var list = new List<NameValuePair>();
        foreach (var name in parameters.AllKeys)
        {
            // escaping occurs both here, and during concatenation
            var value = Uri.EscapeDataString(parameters[name]);
            var item = new NameValuePair {Name = name, Value = value};
            
            // Ensure duplicates are not included
            if(list.Contains(item))
            {
                throw new ArgumentException(
                    “Cannot add duplicate parameters”);
            }
            list.Add(item);
        }
            
        list.Sort((left, right) =>
                      {
                          if (left.Name.Equals(right.Name))
                          {
                              return left.Value.CompareTo(right.Value);
                          }
            
                          return left.Name.CompareTo(right.Name);
                      });
            
        foreach (var item in list)
        {
            sb.Append(item.Name + “=” + item.Value);
            if (list.IndexOf(item) < list.Count – 1)
            {
                sb.Append(“&”);
            }
        }
             
        return sb.ToString();
    }
    private class NameValuePair
    {
        public string Name { get; set; }
        public string Value { get; set; }
            
        public bool Equals(NameValuePair other)
        {
            return Equals(other.Name, Name) &&
                   Equals(other.Value, Value);
        }
            
        public override bool Equals(object obj)
        {
            return Equals((NameValuePair) obj);
        }
    }}
By assembling all query parameters and public OAuth parameters into a single concatenated string, including a normalized URL and the request’s HTTP method, you have constructed the signature base necessary to sign your web requests and use OAuth correctly. The following utility method can perform the final task of bringing all these OAuth processes together to build the signature base.
using System;
using System.Text;
            
public static class OAuth
{
    public static string ConcatenateRequestElements
      (string method, string url, string parameters)
    {
        // URL encode base elements
        url = Uri.EscapeDataString(url);
        parameters = Uri.EscapeDataString(parameters);
            
        // build signature base according to spec
        var sb = new StringBuilder();
        sb.Append(method.ToUpper()).Append(“&”);
        sb.Append(url).Append(“&”);
        sb.Append(parameters);
            
        return sb.ToString();
    }
}
OAuth supports three types of signature encryption: plain strings over HTTPS, HMAC-SHA1, and -RSA-SHA1. Twitter, as with many popular web services utilizing OAuth, only supports HMAC-SHA1 encryption. The .NET Framework provides security classes to ease the burden of working with hashing algorithms. Using the signature base and a chosen cryptography service provider, you can create an OAuth signature by generating a key from the consumer and token secrets concatenated together with a & character, and then using that key with the chosen cryptography algorithm to sign the signature base. If you don’t have a token secret at the time you need to create a signature, you must still provide a placeholder & between the consumer secret you do have, and an empty string where the token secret would go. The following code shows how to create an OAuth signature using HMAC-SHA1.
using System;
using System.Text;
using System.Security.Cryptography;
            
public static class OAuth
{
    public static string CreateSignature
(string signatureBase, string consumerSecret, string tokenSecret)
    {
        if (tokenSecret == null)
        {
            // the token secret is unknown
            tokenSecret = string.Empty;
        }
            
        // URL encode key elements
        consumerSecret = Uri.EscapeDataString(consumerSecret);
        tokenSecret = Uri.EscapeDataString(tokenSecret);
            
        // initialize the cryptography provider
        var key = String.Concat(consumerSecret, “&”, tokenSecret);
        var keyBytes = Encoding.UTF8.GetBytes(key);
        var signatureMethod = new HMACSHA1(keyBytes);
            
        // create a signature with the base and provider
        var data = Encoding.ASCII.GetBytes(signatureBase);
        var hash = signatureMethod.ComputeHash(data);
        var signature = Convert.ToBase64String(hash);
            
        // You must encode the URI for safe net travel
        signature = Uri.EscapeDataString(signature);
        return signature;
    }
}
You now have a set of tools you can use to construct the necessary data required to send an OAuth-authorized request. When you are able to prepare this information, you need to attach it to outgoing requests to participate in an OAuth workflow with Twitter.
Sending OAuth Credentials
There are two options for sending OAuth parameters in your web requests: through the authentication header, similar to how Basic authentication performs, or directly in the URL query string, equivalent to any pre-existing parameters that you are sending outside OAuth. Remember that any parameters you provide in the URL must match the parameters included in the OAuth signature signing process; you cannot add parameters after the fact.
To send OAuth parameters in the authorization header, you must provide the name of the authentication scheme, followed by a list of OAuth key value pairs separated by carriage returns, where the value is enclosed in quotation marks. The following code demonstrates how to set the authorization header with this data for a typical request to retrieve a request token—covered in the next section.
var url = “http://twitter.com/oauth/request_token”;
            
// create oauth parameters
var timestamp = OAuth.CreateTimestamp().ToString();
var nonce = OAuth.CreateNonce();
var oauthParameters = new NameValueCollection
{
    {“oauth_timestamp”, timestamp},
    {“oauth_nonce”, nonce},
    {“oauth_version”, “1.0”},
    {“oauth_signature_method”, “HMAC-SHA1”},
    {“oauth_consumer_key”, “key”}
};
            
// prepare a signature base
url = OAuth.NormalizeUrl(url);
var parameters = OAuth.NormalizeRequestParameters(oauthParameters);
var signatureBase = OAuth.ConcatenateRequestElements(“GET”, url, parameters);
            
// obtain a signature and add it to oauth header parameters
var signature = OAuth.CreateSignature(signatureBase, “secret”, null);
oauthParameters.Add(“oauth_signature”, signature);
            
// build request authorization header
var header = new StringBuilder();
header.Append(“OAuth realm=”Twitter API” “);
for(var i = 0; i < oauthParameters.Count; i++)
{
    var key = oauthParameters.GetKey(i);
    var pair = key + “=”” + oauthParameters[key] + “””;
            
    header.Append(pair);
    if(i < oauthParameters.Count – 1)
    {
        header.Append(“,”);
    }
}
            
// create a new request and set the authorization header
var request = (HttpWebRequest)WebRequest.Create(url);
request.Headers[“Authorization”] = header.ToString();
            
            
Providing OAuth parameters in the request URL is similar to setting the previous authorization header; you obtain the OAuth parameters and generate a signature in the same way, but add the parameters as name value pairs in the query, as per URI specifications.
// build URI
var query = new StringBuilder();
for (var i = 0; i < oauthParameters.Count; i++)
{
    var key = oauthParameters.GetKey(i);
    var pair = key + “=” + oauthParameters[key];
            
    query.Append(pair);
    if (i < oauthParameters.Count – 1)
    {
        query.Append(“&”);
    }
}
            
// URL encode the query
var queryString = Uri.EscapeDataString(query.ToString());
            
// build the URL with query string and use to create a request
url = String.Concat(url, “?”, queryString);
var request = (HttpWebRequest)WebRequest.Create(url);      
You can assemble web requests that use OAuth authentication. Now you can participate in the OAuth workflow, exchanging tokens and obtaining user authorization for your application.

To be continued: See article: "The OAuth Workflow"

Tags:

Comments

2 responses to “Twitter Development: The OAuth Specification”

  1. Anonymous says:

    Note that Uri.EscapeDataString() does not work correctly for multibyte characters. Instead, you must encode each byte of the character. Sample code:

     

    StringBuilder sb = new StringBuilder(s.Length);

    for (int i = 0; i < s.Length; i++)
    {
    if (NoEncodeChars.IndexOf(s[i]) == -1)
    {
    // character needs encoding
    byte[] characterBytes = Encoding.UTF8.GetBytes(s[i].ToString());
    for (int b = 0; b < characterBytes.Length; b++)
    {
    sb.AppendFormat(CultureInfo.InvariantCulture,
    "%{0:X2}",
    characterBytes[b]);
    }
    }
    else
    {
    // character is allowed
    sb.Append(s[i]);
    }
    }

    return sb.ToString();

     
    Where NoEncodeChars is:

    private const string NoEncodeChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
     

  2. Anonymous says:

    There is a bug in this code in the NormalizeRequestParameters if you end up using to talk to other oauth services, as the oAuth spec requires lexographical sort ordering: http://oauth.net/core/1.0/#anchor14
    The fix is pretty easy. Make the following change:
                list.Sort((left, right) =>
                              {
                                  if (left.Name.Equals(right.Name))
                                  {
                                      //return left.Value.CompareTo(right.Value);
                                      return string.Compare(left.Value, right.Value, StringComparison.Ordinal);
                                  }

                                  //return left.Name.CompareTo(right.Name);
                                  return string.Compare(left.Name, right.Name, StringComparison.Ordinal);
                              });

Leave a Reply to Anonymous Cancel reply

Your email address will not be published. Required fields are marked *