Twitter OAuth Example

This tutorial is for programmers who are struggling with code that needs to interface directly with an OAuth flow. This is not a general introduction to OAuth.

Yahoo, Google, Twitter and LinkedIn are just some of the services that use OAuth for secure API calls.

The OAuth flow can seem byzantine with multiple steps that must be completed before you can make a signed API call.

Some OAuth write ups give example request and responses, while others includes almost fully functioning applications; neither work well when you need to debug why your own implmentation of OAuth does not work.

This tutorial contains real code that prints actual OAuth requests and responses as plainly as possible.

I have broken down the Twitter OAuth flow into individual, self-contained PHP scripts that can be run from the command line, from requesting a token to making a signed API call.

My scripts are deliberately devoid of any web session interactions, database interactions, or even command line option handling, just to keep them as simple as possible. It’s straight, standalone procedural code, no objects. If you want to change something, just edit the code and run it.

The goal of the code is to be as transparent as possible, to show you exactly what is sent and what is received. If you look at the code in getreqtok.php, getacctok.php, and tweet.php, you can see that most of them can be combined easily into one function, but they are separated for clarity.

Step 1

Register an application with twitter.

Output: consumer key and consumer secret.

This is self explanatory and requires no code. At the end of the process, you will have a consumer key and a consumer secret. Please keep the consumer secret secret; it is used to sign all of your requests to Twitter.

Step 2

Get a request token.

input: consumer key and secret
output: token and secret to use for Authentication or Authorization

Getting a “request token” is the first step on the road to making a signed API call. If a user has never granted your application permission to do something on his behalf, this is where you start.

Take a look at the getreqtok.php script. The get_request_token function is straight forward. You’ll find most of the supporting functions in either globals.php or oauth_helper.php.

Remember to set the OAUTH_CONSUMER_KEY and OAUTH_CONSUMER_SECRET in globals.php before running the script.

Since this is a command line script, just run it like this
php getreqtok.php

Change the $debug variable from 0 to 1 in globals.php. Rerun the script, and observe the raw request and header and response.

Flip the value of the $passOAuthInHeader boolean and see how things differ when OAuth credentials is passed via query parameters v.s. HTTP headers.

For the purposes of this tutorial, leave the $callback parameter as the string ‘oob’. We will authenticate as a desktop client.

A successful response to this step will contain a [request] token, a [request] token_secret. At the end of the script, it will print a URL to cut-and-paste into your browser. See Step 3, next.

Step 3

Authenticate the user and get the user authorization for your application.

input: URL from Step 2
output: oauth_verifier

There is no code here. For the purposes of this tutorial, cut-and-paste the url from the conclusion of step 2 into your browswer.

At the conclusion of this step, your browser should display a short number, called an “oauth verifier”. Copy this value down and insert it into the getacctok.php script to be used in step 4.

Step 4

Get the access token that enables making API calls.

input: consumer key and secret (step 1),
input: request_token and secret (step 2),
input: oauth_verifier (step 3)
output: access_token, access_token_secret, session_handle

You’re almost there. At the conclusion of step 4, you will receive an access token that allows you to call all the API end points of Twitter.

The script getacctok.php demonstrates how to perform this step. It is very similar to the getreqtok.php script from step 2. Only the inputs are different. Fill in the request token and request token secret from step 2, and the oauth verifier from step 3, and run the script.

In the script’s output, note the [access] oauth_token, [access] oauth_token_secret, user_id, and screen_name. The access token and secret will be used to sign API calls. Twitter does not expire this access token, so you will not have to go through a token refresh step.

Making an API call

Posting a Tweet.

input: post data
input: consumer key and secret (step 1)
input: access_token and secret (step 4)
output: response from api call

This is the moment you have been waiting for. Actually making an API call to post a tweet. Once you have the access token and secret from Step 4, you can keep using them over and over again for all API calls.

This is the step where most people run into trouble, because this step requires mingling parameters that are not OAuth related with those that are.

Look at the tweet.php script. It posts the string ‘Hello World!’ to your twitter status. Edit the script and enter the access token, access token secret, and any tweet you want to post.

Run it, and refresh your twitter home page.

Congratulations!

Note that you should not post the exact same message more than once; twitter will reject the second one as a duplicate.

HMAC-SHA1 signature

The OAuth HMAC signture generation step that trips most people up is the generation of the Signature Base String, which requires some esoteric “normalization” operation. You can read about it from the spec directly.
http://oauth.net/core/1.0/#rfc.section.9.1.1
http://oauth.net/core/1.0/#rfc.section.A.5.1

A Signature Base String is a RFC 3986 encoded concatenation of:

  1. An uppercased HTTP method: GET, POST, etc.
  2. An ‘&’
  3. A url without any parameters (scheme://fqdn/path)
  4. An ‘&’
  5. All the query parameters (combined for both the API call and those for oauth) encoded according to rfc 3986 AND sorted by key. DO NOT include the oauth_signature parameter in this step, because you are generating the oauth_signature.

If you read that last paragraph carefully, you’ll note that the stuff in a signature base string is double rfc 3986 encoded.

A Signature Key is the concatenation of:

  1. rfc 3986 encoded consumer_secret
  2. ‘&’
  3. rfc 3986 encoded token_secret

Then you apply the hmac-sha1 function using the signature_key to hash the signature_base_string and finally base64_encode the whole thing:

$sig = base64_encode(hash_hmac('sha1', $signature_base_string, $signature_key))

Odds and ends to keep in mind

  1. Note that the sorting and encoding of query parameters is done both when constructing the query parameter string, and when constructing the HMAC signature base string.
  2. When normalizing the signature base string, the ‘?’ separating the url from the parameters (if you’re using GET) is replaced with a ‘&’.
  3. If you are using POST instead of GET, make sure that the HTTP method string is POST when normalizing the signture base string.
  4. Make sure that you are using the token secret that goes with the correct token.
  5. If you get signature invalid error, check your Signature Base String normalization code. Be careful of the deliberate double encoding.
  6. When debugging, output what you are actually passing to and receiving from the server. Try capturing the actual data that is leaving your machine. If you are testing a web application in Firefox, the Live HTTP Header plugin is great for this.

47 thoughts on “Twitter OAuth Example

  1. Pingback: Tweets that mention Twitter OAuth Example « Nothing of Value -- Topsy.com

  2. Wow, I can’t thank you enough for taking the time to write this up. Now that I’ve followed your tutorial I can see that there’s no way in hell that I could have figured this out by myself. You have probably saved me two fruitless days of blind stumbling.

  3. Ha, well unlike Gabe above, I actually did have two fruitless days of blind stumbling. I am extremely happy, however, to have finally found this page and got everything working at last. Thanks Joe!

  4. Perhaps may be of interest of visitors, and not so, to know that I have written a twibot with OAUTH autentication, and have explained how to do it in my blog at:

    http://redacacia.wordpress.com

    Until a few days ago, I was desperate in decifrating the usage of Twitter’s OAUTH “puzzle”.

    I have done it my way, as I have explained in my blog. By sharing the code perhaps more of rssfeed bots may continue to twit after Twitter’s August 16th, 2010 programmed fasing out of basic authentication.

    Tayeb
    in sunny Portugal!

  5. Thanks a lot, I’m using twitteroauth (http://abrah.am) and I was getting a signature invalid error when I was trying to get home_timeline, it only worked when I was getting user_timeline (unauthenticated method), but after following this steps and your scripts I noticed that I was using the wrong API codes: consumer_key and secret (I registered two applications) so the one I was testing didn’t have the correct tokens.

    Thanks again

  6. Great stuff – perfect for replacing my old Nagios alert scripts :D

    I modified tweet.php slightly to accept the tweet on the command line:

    $tweet = $argv[1];
    if (empty($tweet)) {
    echo “Tweet text missing.\n”;
    exit(1);
    }

  7. Thank you so much!
    But I have one problem: Everytime I try to send a tweet with the german characters “ä, ö, ü” I get the error message “Invalid signature”.
    I have tried to understand the PHP-Script but I still don’t know… It would be cool if someone could answer a solution :)

    ^x3ro

  8. @^x3ro your problem is probably with the url encoding of the status update.
    I’ve found that the actual tweet has to be double url encoded and there is also a small glitch in php.
    If you use urlencode() in php it will convert blank spaces to + instead of %20.
    My solution was to handle the status update separate from the rest of the data. So I would have something like this:

    $status = “&status=” . str_replace(“+”,”%20″,urlencode(put your tweet here));
    $data = urlencode(“http://twitter.com/statuses/update.xml/&all_that_other_junk” . $status)

    As you can see the “tweet” portion gets urlencoded twice and both + and ” ” are converted to %20 before the second urlencode.

    A result would look like this…

    POST&http%3A%2F%2Ftwitter.com%2Fstatuses%2Fupdate.xml&oauth_consumer_key%3AbcdVLSZFBLrabcdmzj2123%26oauth_nonce%3Dd41d8cd98f00b204e9800998ecf8427e%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1284288300%26oauth_token%3D15689754-abcdfghj5ITfAW8ny95adfM3fqHUEIxx7jadeftbh%26oauth_version%3D1.0%26status%3DLast%2520tweet%2520%253B%252F%2520I%2527m%2520spent.

    I hope this helps :)

  9. Thank you so much for this information. I am not an expert in anything web-wise at all. I had done a small bit of php and cut and pasted code before to enable tweets to be sent when people visted my site.

    This all looked daunting to start with. But I took a big breath and followed the steps parrot fashion and it worked a treat.

    Now I just need to incorporate what I’ve learnt here to my own code.

    Again, thank you!

  10. This day i had to smile. They still exist, the ‘hello world’ guys. You made my day and your blog post is extremely helpfull and to the point, just explaning the stuff that MATTERS and getting rid of the bells and whistles.

    I googled: oauth twitter status update example simple

    And i found: http://www.maxqubit.com/files/helloworld.jpg

    Thx,
    Max

  11. hello,
    I want to know that there is one parameter name: signature
    to generate signature which data required as argument in method.
    i have method which return me signature but i dont know what i have to give in argument.

  12. Joe,
    Your straigtforward example allowed me to quickly set up a tweet interface for my client. Ten minutes after I found your page I had everything up and running… this after four hours of time wasted visiting many other websites trying to figure this out. THANK YOU!

  13. Wow…!!! This article is gr8…help me to first tweet on my account via application…Cheers..!!!

  14. Your tutorial saved my life…Thank you very much..

    Does that mean I dont have to login to twitter to post tweets the second time (once i authorize the app)

    Also, does the auth_token and secret expire anytime?

    Thanks again!!!

    Joel

    • Joel, once the app is authorized it’s good until it is unauthorized – no need to relogin. The last time I checked (maybe a year ago), auth token and secret do not expire for twitter.

  15. @joec

    Wow, this was exactly what I needed and it worked perfectly — once I figured out what the steps were. As a fellow hacker/coder, my suggestions for the documentation and code are made with the utmost of respect. I know how much work you put into this and want only to help you make it even more useful to others.

    Code suggestions:
    I believe the output of each piece of code should be formatted the way the next piece of code wants to see it. For example, I think it would help if the output of the getreqtok.php was this:

    $request_token=’blahblah';
    $request_token_secret=’blahblah';
    instead of this:
    [oauth_token] => blahblah
    [oauth_token_secret] => blahblah

    Not only does it make cutting and pasting the results into the next piece of code easier, it also helps people to understand it easier, as the variable names match. The same should be true of the output of getacctok.php.

    Documentation suggestions:

    I like how you have main, step 1/2/3 headings. The challenge is that there are several sub-steps within each step that I also think you should document. Here are my suggestions by section.

    I believe you should make a general note at the beginning of each section that they should save the outputs from each step as they occur. Save them in a document for later cutting and pasting.

    Step 1
    The values of these fields were not self-explanatory to me I believe you should explain what to put in the various fields.
    – Step 1a: fill in the fields
    – Step 2a:get the values from this field and that field
    – Step 2b:store those values in a doc somewhere
    Step 2
    – Step 2a: edit globals.php and put in the right values for request_token and request_token_secret
    – Step 2b: php getreqtok.php
    – Step 2c:store the values of oauth_token and oauth_token_secret and the URL
    Step 3
    – Step 3a:Login to twitter.com via the web as the user you want to do automatic tweets as
    – Step 3b:Cut and the URL from step 2c into your browser
    – Step 3c:Click authorize app
    – Step 3d:Store the large numeric value on screen as the value of oath_verifier
    Step 4
    – Step 4a:Edit getacctok.php and change the values of request_token, request_token_secret, and oauth_verifier using the info from Steps 2 and 3.
    – Step 4b:php getacctok.php
    – Step 4c:Store the values for oauth_token and oauth_token_secret (although I believe the code and doc should call them access token and access token secret, as that’s what they’re called in the next script)
    Step 5
    – Step 5a: Edit tweet.php and replace the values for $access_token and $access_token_secret with the values you recorded in steps 4c
    – Step 5b: Change what the tweet message will say by changing the value of $tweet
    – Step 5c: Make sure you are running a twitter client or watching twitter.com and following the user you are about to send a tweet as
    – Step 5d: php tweet.php (You should see your tweet show up in your twitter client or on twitter.com)
    – Step 5e: Edit tweet.php and change the value of $tweet
    – Step 5f: php tweet.php (You should see your second tweet show up as well.)

    I hope this is helpful.

    I again want to thank you, as this has been extremely helpful to me and given me exactly what I need.

  16. I’m receiving an error with tweet.php

    {
    “error”: “Invalid \/ expired Token”,
    “request”: “\/1\/statuses\/update.json”
    }

  17. I needed to do this multiple times in order to automate tweets from several accounts, so I found it easier to add some command-line parsing and changed what the scripts display. This version doesn’t require anyone to edit any files in order to make it work. Here’s the way it looks now on my end. (Of course, I have substituted my actual values with variables in all caps. In my version, it displays the actual keys, etc. to you directly.) If you’d like to see my changes, just drop me a line at curtis -> at <- backupcentral com. Thanks again for all your hard work.

    # php getreqtok.php CONSUMER_KEY CONSUMER_KEY_SECRET

    1. Login to twitter.com as the user you want to send automated tweets from.
    2. Point web browser to the following URL:

    http://api.twitter.com/oauth/authorize?oauth_token=OAUTH_TOKEN&oauth_token_secret=OAUTH_TOKEN_SECRET&oauth_callback_confirmed=true

    3. Click on “Authorize App” at twitter.com and observe the value it gives you.
    4. Replace [AUTHKEY] in the following command with the value that twitter gives you:

    php getacctok.php CONSUMER_KEY CONSUMER_KEY_SECRET OAUTH_TOKEN OAUTH_TOKEN_SECRET [AUTHKEY]

    # php getacctok.php CONSUMER_KEY CONSUMER_KEY_SECRET OAUTH_TOKEN OAUTH_TOKEN_SECRET AUTHKEY

    Run the following command:
    php tweet.php CONSUMER_KEY CONSUMER_KEY_SECRET OAUTH_TOKEN OAUTH_TOKEN_SECRET “Hello World”

    # php tweet.php CONSUMER_KEY CONSUMER_KEY_SECRET OAUTH_TOKEN OAUTH_TOKEN_SECRET “Hello World”

    {
    “geo”: null,
    “user”: {
    “is_translator”: false,
    “protected”: false,
    “url”: null,
    “default_profile”: true,
    “follow_request_sent”: false,
    “profile_text_color”: “333333”,
    “name”: “name”,
    “show_all_inline_media”: false,
    “contributors_enabled”: false,
    “geo_enabled”: false,
    “created_at”: “Sun Jun 19 05:31:44 +0000 2011″,
    “profile_sidebar_fill_color”: “DDEEF6″,
    “id_str”: “320034107”,
    “following”: false,
    “notifications”: false,
    “profile_background_tile”: false,
    “utc_offset”: null,
    “description”: null,
    “listed_count”: 0,
    “statuses_count”: 17,
    “profile_link_color”: “0084B4″,
    “favourites_count”: 0,
    “profile_sidebar_border_color”: “C0DEED”,
    “followers_count”: 1,
    “profile_image_url”: “http:\/\/a0.twimg.com\/sticky\/default_profile_images\/default_profile_1_normal.png”,
    “time_zone”: null,
    “id”: userid,
    “verified”: false,
    “profile_use_background_image”: true,
    “screen_name”: “userid”,
    “default_profile_image”: true,
    “lang”: “en”,
    “profile_background_color”: “C0DEED”,
    “location”: null,
    “profile_background_image_url”: “http:\/\/a0.twimg.com\/images\/themes\/theme1\/bg.png”,
    “friends_count”: 0
    },
    “created_at”: “Mon Jun 20 06:52:32 +0000 2011″,
    “id_str”: “82702356206206976”,
    “coordinates”: null,
    “text”: “Hello World…”,
    “favorited”: false,
    “in_reply_to_status_id_str”: null,
    “in_reply_to_screen_name”: null,
    “in_reply_to_user_id_str”: null,
    “truncated”: false,
    “id”: 82702356206206976,
    “contributors”: null,
    “retweeted”: false,
    “source”: “\u003Ca href=\”http:\/\/oururl\” rel=\”nofollow\”\ourapp\u003C\/a\u003E”,
    “retweet_count”: 0,
    “place”: null,
    “in_reply_to_user_id”: null,
    “in_reply_to_status_id”: null
    }

  18. Pingback: How to “Sign in with Twitter” using twitter4j | the wanderer

  19. Great tutorial! By the way, how come I get an Error after I refresh the page on getting the access token? At first, it shows the array of values
    Array ( [oauth_token] => ****** [oauth_token_secret] => ******** [user_id] => 1234 [screen_name] => john ), but after I refresh I get this error
    ( [ “1.0” encoding=”UTF-8″?> Invalid / expired Token/oauth/access_token )

  20. Sorry, but my vim editor seems to have an outdated version of the php lexicon. I can now see the functions as being php ones.

  21. HI,

    It worked fine for me for posting tweets but when i replaced the url to fetch friends ids with GET request. it returned an empty array.

    I m doing something wrong which i m not able to figure out. can you help me out in this. I can send you the function i created for fetching friends ids.

    Thanks

  22. I have an error:
    Notice: Undefined offset: 1 in C:\myprojectpath\oauth_helper.php on line 73

    In $url i don’t have anu value after the question mark and php_url_query is null.

    Please Help!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s