Understanding OAuth – Tweeting from Scratch, Part 1

This entry is part 1 of 2 in the series Understanding OAuth - Tweeting from Scratch

Understanding OAuth - Tweeting from Scratch

A common complaint about OAuth is that it is very difficult to understand, but perhaps some of that confusion is because of an expectation that the abstraction provided by a third-party library will erase the need to understand the steps of an OAuth transaction – it does not. This two-part article demonstrates how how OAuth v1 works by explaining the process of connecting a PHP application to the Twitter API using only a few built-in functions to post a message to a user’s Twitter stream.

The example code provided in this article is intended for educational purposes only. It lacks much practical use and error handling, Any real development should take advantage of an existing OAuth library, but after reading this article you’ll have a better understanding of how OAuth works and will be better equipped to troubleshoot any problems that may arise.

Obtaining Consumer Credentials

Posting to Twitter on behalf of a user without asking for a username and password combination every time requires a sort of “valet key”… this key is OAuth. In OAuth terms, your client application is called the consumer, the user is called the resource owner, and Twitter is the server or service provider.

Before Twitter will accept a post from your application, you need to obtain your own client credentials: the Consumer Key and Consumer Secret. Twitter, like most services that provide a public API, will grant you a key and secret after completing a registration form (available at dev.twitter.com/apps). With Twitter, you need to supply some information that will identify your application to the user during the authorization process, specifically its name, description, and website URL. You also need to enter a Callback URL, which I’ll explain later.

Once you have submitted the Create an Application form, you will be taken to a Details page for your new application. Scroll down to find your Consumer Key and Secret. You’ll need these along with the handful of endpoint URLs that are listed. Notice the application is granted Read Only access by default; since it will be posting tweets, you need to click the Settings tab and change the access to “Read and Write”.

Authorizing the Application

The Consumer Key and Consumer Secret allow your application to talk to the Twitter API, but those alone won’t let it post tweets on another user’s behalf. You need access credentials: an Access Token and Access Secret. To obtain these through a request to the Service Provider (Twitter) in a short conversation using the consumer credentials involving Twitter and the end-user. Obtaining the access credentials can be fairly painful, but luckily you only need to do this once per user. You can save the credentials indefinitely for later use and not require the user to reauthorize your application. The conversation requesting the access credentials requires its own set of credentials: the Request Token and Request Secret.

Authorization Step 1: Request Credentials

The authorization process typically starts by directing the user to an Authorize Twitter page. This is a page you create that initiates the request for a Request Token from Twitter and begins the OAuth authorization process. It must generate a URL used to grab the Request Credentials, and once you have the credentials you can redirect the user to Twitter to grant the application permission to post.

Requesting the Request Credentials requires a signed request, which means you need to send an OAuth signature along with other important parameters in the request. The signature is a base64-encoded hashed list of the request parameters. In Twitter’s case, the hashing algorithm is HMAC-SHA1. The signing process prevents anyone else from posing as your application with your credentials even though the Consumer Key is transmitted in plain text. The signature can only be reproduced by you (the Consumer) and the server (Twitter) because you two are the only entities that should know the Consumer Secret that hashes the signature.

Lets build a string the signature is based on:

<?php
$requestTokenUrl = "http://api.twitter.com/oauth/request_token"; 
$authorizeUrl = "http://api.twitter.com/oauth/authorize";
$oauthTimestamp = time();
$nonce = md5(mt_rand()); 
$oauthSignatureMethod = "HMAC-SHA1"; 
$oauthVersion = "1.0";

$sigBase = "GET&" . rawurlencode($requestTokenUrl) . "&"
    . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
    . "&oauth_nonce=" . rawurlencode($nonce)
    . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
    . "&oauth_timestamp=" . $oauthTimestamp
    . "&oauth_version=" . $oauthVersion); 

Some of the above variables may be fairly obvious – $requestTokenUrl was obtained from Twitter when you were granted consumer credentials, and $oauthTimestamp is the current UNIX epoch time. The less obvious item is $nonce, which is nothing more than a random string used only once (a difference nonce is used for each transaction). Typically an MD5-hashed random number works fine as a nonce. There is also $oauthSignatureMethod which is always HMAC-SHA1 for Twitter, and the $oauthVersion which is currently v1.0 for Twitter.

Next, the string for the signature is constructed as $sigBase. OAuth says that the signature base must be the HTTP method (GET in this case) followed by an “&”, followed by the URL-encoded request URL ($requestTokenUrl) followed by another “&”, and finally a URL-encoded and alphabetized list of parameter key/value pairs (the values of which must also be encoded) separated by “&”s. Note that when OAuth wants something URL-encoded, it means RFC-3986. PHP’s rawurlencode() function works because it encodes spaces as “%20″ instead of “+” as urlencode() does.

You also need a Signing Key. The key is always the Consumer Secret followed by an “&” and either 1) the OAuth Token Secret (which is part of the Token Credentials you don’t have yet), or 2) nothing. Then you can generate the final signature using PHP’s built-in hash_hmac() function.

<?php
$sigKey = $consumerSecret . "&"; 
$oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));

You put all the pieces together to construct a URL to request the Request Credentials:

<?php
$requestUrl = $requestTokenUrl . "?"
    . "oauth_consumer_key=" . rawurlencode($consumerKey)
    . "&oauth_nonce=" . rawurlencode($nonce)
    . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
    . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
    . "&oauth_version=" . rawurlencode($oauthVersion)
    . "&oauth_signature=" . rawurlencode($oauthSig); 

$response = file_get_contents($requestUrl);

You’ll want a lot more error handling for anything beyond this simple demonstration, but for now I assume nothing will ever go wrong and you are able to receive the temporary Request Credentials in $response.

The response sent back from Twitter looks like this:

oauth_token=mjeaYNdNYrvLBag6xJNWkxCbgL5DV6yPZl6j4palETU&oauth_token_secret=W45dnBz917gsdMqDu4bWNmShQq5A8pRwoLnJVm6kvzs&oauth_callback_confirmed=true

The oauth_token and oauth_token_secret values are extracted from the response and are used to construct the next link the user goes to for the second step in the authorization process. It’s a good idea to store the Request Credentials in the user’s session so they are available when the user returns from Twitter’s authorization page. The Authorization URL is provided on the Details page after registering your application with Twitter.

<?php
parse_str($response, $values);
$_SESSION["requestToken"] = $values["oauth_token"];
$_SESSION["requestTokenSecret"] = $values["oauth_token_secret"];

$redirectUrl = $authorizeUrl . "?oauth_token=" . $_SESSION["requestToken"]; 
header("Location: " . $redirectUrl);

Now that the application can send users to Twitter for authorization, it is a good time to add a Callback URL so Twitter can send them back to the application! The Callback URL is simply an address that Twitter will direct the user to after he has authorized your application to send tweets on his behalf, and is specified on the Detail page’s Settings tab.

When Twitter redirects the user to the Callback URL, it appends two additional parameters: your oauth_token from the initial request which can be used for verification, and oauth_verifier which is used in obtaining the Authorization Credentials.

Of the three sets of credentials needed to post tweets, you now have two – the Consumer Credentials and the Request Credentials. Next up: Access Credentials!

Authorization Step 2: Access Credentials

To obtain the Access Credentials you need the oauth_token, oauth_token_secret, and the newly obtained oauth_verifier. This step requires another signed request, this time to the Access Token URL displayed on the Details page.

<?php
$oauthVersion = "1.0";
$oauthSignatureMethod = "HMAC-SHA1"; 
$accessTokenUrl = "http://api.twitter.com/oauth/access_token"; 
$nonce = md5(mt_rand()); 
$oauthTimestamp = time();
$oauthVerifier = $_GET["oauth_verifier"]; 

The $accessTokenUrl is the next endpoint obtained from the Details page. A new $oauthTimestamp and $nonce are generated, and $oauthVerifier is sent back from the Twitter Authorization page. Not listed, but in the $_SESSION array, are the request credentials from the previous step that are also required.

This step of the authorization process requires another signed request. Once the signature is built, it is used with the request for Access Credentials.

<?php
$sigBase = "GET&" . rawurlencode($accessTokenUrl) . "&"
    . rawurlencode("oauth_consumer_key=" . rawurlencode($consumerKey)
    . "&oauth_nonce=" . rawurlencode($nonce)
    . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
    . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
    . "&oauth_token=" . rawurlencode($_SESSION["requestToken"])
    . "&oauth_verifier=" . rawurlencode($oauthVerifier)
    . "&oauth_version=" . rawurlencode($oauthVersion)); 
$sigKey = $consumerSecret . "&"; 
$oauthSig = base64_encode(hash_hmac("sha1", $sigBase, $sigKey, true));

$requestUrl = $accessTokenUrl . "?"
    . "oauth_consumer_key=" . rawurlencode($consumerKey)
    . "&oauth_nonce=" . rawurlencode($nonce)
    . "&oauth_signature_method=" . rawurlencode($oauthSignatureMethod)
    . "&oauth_timestamp=" . rawurlencode($oauthTimestamp)
    . "&oauth_token=" . rawurlencode($_SESSION["requestToken"])
    . "&oauth_verifier=" . rawurlencode($oauthVerifier)
    . "&oauth_version=". rawurlencode($oauthVersion)
    . "&oauth_signature=" . rawurlencode($oauthSig); 

$response = file_get_contents($requestUrl);

This time $response has the very useful screen_name, user_id and the much awaited Access Credentials!

Summary

This concludes the authorization part of this article. So far you’ve learned how to create a new Twitter application and use the provided consumer credentials to step through the OAuth “dance” to obtain the access credentials. In the second and final part of this series I’ll discuss using the access credentials to post a tweet to the user’s Twitter stream.

Image via Quin / Shutterstock

Understanding OAuth - Tweeting from Scratch

Understanding OAuth – Tweeting from Scratch, Part 2 >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Moshe Teutsch

    Interesting post. Looking forward to part 2!

  • http://blazejklisz.pl Błażej Klisz

    There should be parse_str instead of parse_string. Nevertheless a high-class tutorial. Thanks!

    • http://zaemis.blogspot.com Timothy Boronczyk

      Woops! Thanks for the catch, Błażej. I’ve made the correction in the article.

  • nydel

    i am discouraged by the pseudo-tutorials provided by popular services for their emerging api – by what the internet has become – by that the most advanced tutorial on twiddler dot com teaching nothing more than the keystrokes for copy and paste.

    the internet has become overrun by ostensible webmasters that operate and profit from factory quasi applications*1^ that they do not understand on the front-end, let alone the back. i worry that, if you could not write facebag yourself, then you likely have no valid business*2^ operating a facebag application, especially in the ever-common cases where the application master isn’t even able to script interaction with the service’s api; come now, most of these people don’t even know what api is. and it’s not their fault entirely, they are encouraged*^ to remain +ignorant*3^ – and this is sad, because it stops evolution, discourages the autodidacticism that should have spread like wildest of wild fire once internet became the antibodies of the bloodstream, from having been the common flu. i see a major revolution on the horizon.

    *1^ the “applications” are not even applications. this deft redefinition of words as to destroy pointers to ideas is a tool of those things that care more about survival than evolution. if “application” means “interaction with the only implementation of twitter by the only implementation of twitter” then these applications are simply clones of a virus that wishes not to die off and be replaced by something better at any cost.

    *2^ unless you can say “i understand myface from the ground up and find it to be the ultimate perfection of internet application” then you should be writing something better, not cloning myface.

    *3^ people are as surprisingly intelligent as they are shockingly ignorant, it may be suggested. look at twitter for example. they intentionally supply you with a shoddily-written php library that prevents you from learning a damn thing – they present this package as a necessity for to have an application & do not even mention “or you could just write the code to your liking” – were they to mention that, well, i don’t think sloppy javascript-heavy (cr)apps like facebag & twiddler are exactly prepared to take on all comers, so they nip the bud & kill curiosity while young.

    so finding this tutorial was very refreshing and i thank you sincerely for putting yourself into it – i /know/ you are not getting praise equal to how much you deserve and that’s //because// this page has true and real information – but please know that — though only one person appreciates this for every ten-thousand that wouldn’t recognize it as /anything/ let alone brilliant — that one person is of the type to appreciate one-hundred-thousand times more sincerely than the others.