I am very familiar with Oauth 2.0, but recently I had to work with Twitter and it was not as smooth of an experience as other providers such as Google and Facebook. This is because Twitter uses Oauth 1.0a and requires request signing.
Oauth 1.0a requires requests to be signed using HMAC and a secret key. Using this protects communication between the client and the server, but how do we accomplish this task?
Creating a signature happens through the following steps:
For the sake if this article, there will be some heavy references to JavaScript, but you can apply this knowledge to any programming language.
Twitter, like all Oauth 1.0a providers have a set of required parameters. They will probably be similar if not the same across all providers. Let’s look at the following required elements:
oauth_consumer_key
oauth_nonce
oauth_signature_method
oauth_timestamp
oauth_version
All of the above properties will have static values provided by the Oauth provider except the oauth_nonce
and oauth_timestamp
. These are dynamic values that must be set during run time. The nonce is a random string value, something I normally set as a hash of the timestamp. The timestamp is the time from epoch.
If using JavaScript, you can get the epoch by running the following:
var oauth_timestamp = Math.round((new Date()).getTime() / 1000.0);
With the required parameters in hand, you can now start collecting the query and body parameters.
All parameters, query and body, must be collected before creating a signature base string. In addition, each of these parameters must be percent encoded. Let’s assume we are doing a POST request and there is only one body parameter called oauth_callback
. Our new parameter list now looks like the following:
oauth_consumer_key
oauth_nonce
oauth_signature_method
oauth_timestamp
oauth_version
oauth_callback
With all parameters collected we can move on to creating a signature base string.
The signature base string has some very strict requirements, but it is the final string that will be hashed using the appropriate hashing algorithm. The base string must be formatted like the following:
POST&REQUEST_URI&PARAMETERS
Let’s assume for this example we are going to use the https://api.twitter.com/oauth/request_token endpoint. The generic format I posted above, is far to general for what we need to accomplish. To be more specific, the request uri and all parameters need to be percent encoded. In addition to being percent encoded, the parameters need to be in alphabetical order. To accomplish this, you might do something like the following in JavaScript:
var signatureBaseString = "POST&" + encodeURIComponent("https://api.twitter.com/oauth/request_token") + "&";
signatureBaseString += encodeURIComponent("oauth_callback=https://www.example.com&");
signatureBaseString += encodeURIComponent("oauth_consumer_key=somekeyhere&");
signatureBaseString += encodeURIComponent("oauth_nonce=qwerty&");
signatureBaseString += encodeURIComponent("oauth_signature_method=HMAC-SHA1&");
signatureBaseString += encodeURIComponent("oauth_timestamp=12345&");
signatureBaseString += encodeURIComponent("oauth_version=1.0");
This is the string that will be used to create a hashed signature.
We will now create a hashed signature string from our signature base string. If you’re using Twitter, the requirement is HMAC-SHA1 which isn’t necessarily the strongest form of SHA, but it works.
Note that you cannot use your raw consumer secret key to sign the request. You must concatenate the secret key and Oauth token secret provided by your Oauth provider for signing. These must be concatenated with an ampersand.
var secret_signing_key = consumer_secret_key + "&" + oauth_token_secret
Above is JavaScript code for creating a secret signing key to be used in the HMAC function. If the oauth token secret does not exist, continue to add the ampersand, but omit the oauth token secret.
Let’s look at the following PHP function:
<?php
$signature = hash_hmac('sha1', $signatureBaseString, 'CONSUMER_SECRET');
?>
If you’re using PHP, don’t forget to convert the HEX $signature string to a BASE64 encoded $signature string. If you wanted to do this with JavaScript, I highly recommend using the open source jsSHA library as it has some really great hashing algorithms including HMAC-SHA1. With jsSHA your signature would be generated like follows:
var shaObj = new jsSHA(signatureBaseString, "TEXT");
var signature = shaObj.getHMAC(encodeURIComponent(secretKey) + "&", "TEXT", "SHA-1", "B64");
The signature might have some slashes and equals sign characters in it. This signature needs to be percent encoded. In JavaScript you can do the following:
var encodedSignature = encodeURIComponent(signature);
With the percent encoded signature string you are now ready to create an authorization header for use with an API.
Now that you have a signature you can format an authorization header. Based on the required, query, and body parameters, our authorization header should look like this:
Authorization: OAuth oauth_consumer_key="keyhere",oauth_timestamp="12345",oauth_signature_method="HMAC-SHA1',oauth_nonce="qwerty",oauth_version="1.0",oauth_signature="signaturehere"
Note that I made up all the values above, but the keys are accurate. Only the required parameters show up in the header. The query and body parameters do not show up in the header, only in the query and body.
In AngularJS, the post request might look something like this:
$http.defaults.headers.post.Authorization = 'OAuth oauth_consumer_key="keyhere",oauth_timestamp="12345",oauth_signature_method="HMAC-SHA1',oauth_nonce="qwerty",oauth_version="1.0",oauth_signature="signaturehere"';
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
$http({method: "post", url: "https://api.twitter.com/oauth/request_token", data: "oauth_callback=http://www.example.com" });
When working with a provider that uses Oauth 1.0a, every request must be signed. I personally find it to be a hassle, but it is sometimes out of our control.
Below is a CodePen.io example that I wrote demonstrating the signing process.