Using OAuth for Twitter Authentication on the BlackBerry PlayBook

OAuth is becoming critical for any app developer who wants to access data from a 3rd party source on behalf of their users. If you aren’t familiar with OAuth there is an excellent intro doc over at Hueniverse. I had some difficulty wrapping my head around OAuth the first few times I tried to use it so I’ll provide a quick explanation below as I walk through the sample.

OAuth is a way of providing access from a 3rd party site (in this case Twitter) to your application without needing the user to type their 3rd party credentials into your application. Instead, OAuth lets you register with the 3rd party site and uses a series of tokens, which eventually give you access to the data. One of the main benefits with OAuth is that if an application does something nefarious with the data, the user can immediately revoke access. For this example I’ll be walking through the Twitter API and using the very excellent OAuth-AS3 library by Shannon Hicks, the founder of Pintley, which you should check out if you’re the kind of person who likes to drink beer.

The first step to using OAuth is to register with the site you want to get data from. Twitter has a very easy way to do this with their App portal. Fill in the necessary info (select the type as client since this won’t be a web-based application) and Twitter will provide a bunch of random strings that you’ll use to request access to the data and then request the data itself. The data that we’re most worried about is in the OAuth 1.0a Settings section and includes a consumer key, consumer secret, request token URL, access token URL, and authorize URL.

Getting information out of Twitter has the following flow:

  1. Use the consumer key and the consumer secret to create an OAuthConsumer object.
  2. Use the OAuthConsumer object to ask Twitter for permission to access data.
  3. Prompt the user to authorize your application to access their specific data.
  4. Request the user’s data and perform operations on their behalf.

This is all done with a series of tokens until the last stage when you get an access token that you will use to access data after the user gives you permission. I set those up as private field variables so they can be accessed from any method in this example and set the unchanging data as static variables.

private static var CONSUMER_SECRET:String = "<YOUR CONSUMER SECRET>";
private static var CONSUMER_KEY:String = "<YOUR CONSUMER KEY>";
private static var REQUEST_TOKEN_URL:String = "https://api.twitter.com/oauth/request_token";
private static var ACCESS_TOKEN_URL:String = "https://api.twitter.com/oauth/access_token";
private static var AUTHORIZE_URL:String = "https://api.twitter.com/oauth/authorize";
private static var API_URL:String = "https://api.twitter.com";
private static var SIGNATURE:OAuthSignatureMethod_HMAC_SHA1 = new OAuthSignatureMethod_HMAC_SHA1();
 
private var _consumer:OAuthConsumer;
private var _authRequest:OAuthRequest;
private var _accessRequest:OAuthRequest;
 
private var _requestToken:OAuthToken;
private var _accessToken:OAuthToken;

The first step is to have our application log into Twitter with the consumer secret and the consumer key so that it can make the authorization request. In the init() function I set up the UI as well as create the consumer and my first OAuthRequest object, the authorization request. This will just go out to Twitter and make sure that the consumer key and secret are registered to an application. If they are, then Twitter will return a request token that I can use to request access from the user for his or her data. In that init() method I have a button that will start the process and the event handler on the button uses the buildRequest method of my _authRequest object to format the URL string correctly and then I send it off to Twitter.

protected function init():void
{    
     _consumer = new OAuthConsumer(CONSUMER_KEY,CONSUMER_SECRET);
     _authRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET,REQUEST_TOKEN_URL,null,_consumer);
 
     _loginContainer = new Container();
     _loginContainer.align = ContainerAlign.MID;
 
 
 
     var button:LabelButton = new LabelButton();
          button.label = "Login to Twitter";
          button.addEventListener(MouseEvent.CLICK,onClick);
 
 
     _loginContainer.addChild(_spacer);
     _loginContainer.addChild(button);
     _loginContainer.setSize(1024,600);
     addChild(_loginContainer);
 
}
 
protected function onClick(event:MouseEvent):void
{
     var urlRequest:URLRequest = new URLRequest(_authRequest.buildRequest(SIGNATURE));
     var loader:URLLoader = new URLLoader(urlRequest);
     loader.addEventListener(Event.COMPLETE,onRequestComplete);
}

With that response I can now build the _requestToken, which I can use to let the user grant me access to his or her data. The next step is to prompt the user to authorize my application. Once I get the response back from Twitter’s servers I save that information as the request token and then build a UI to let the user start the authorization process.

protected function onRequestComplete(event:Event):void
{
     _requestToken = getTokenFromResponse(event.currentTarget.data);
 
     _authContainer = new Container();
     _authContainer.align = ContainerAlign.MID;
 
 
     var authBtn:LabelButton = new LabelButton();
          authBtn.label = "Authorize this application";
          authBtn.addEventListener(MouseEvent.CLICK,onAuthClick);
 
     _authContainer.addChild(_spacer);
     _authContainer.addChild(authBtn);
     _authContainer.setSize(1024,600);
 
     removeChild(_loginContainer);
     addChild(_authContainer);
}

When the user clicks the button my application will send them to Twitter where they will have to log in and authorize my application to use their data. They’re prompted either to deny my app or allow it.

Twitter Authorization Form

If they click Allow, then Twitter provides a PIN number that the user has to enter back in the application to complete the authorization process. For web-based applications there is a callback URL which is where the user is sent after they authorize the application. But for client-side applications, Twitter uses the PIN number and asks the user to enter it back in the application.

Getting the PIN Number

In the code, I first create the UI elements I need to track the PIN number and then make a URLRequest using the key that Twitter provided in the request token.

protected function onAuthClick(event:MouseEvent):void
{
     _verifyContainer = new Container();
     _verifyContainer.align = ContainerAlign.MID;
 
     var label:Label = new Label();
          label.size = 100;
          label.sizeUnit = SizeUnit.PERCENT;
          label.text = "Enter the PIN from Twitter.com";
 
     var font:TextFormat = new TextFormat();
          font.align = TextFormatAlign.CENTER;
          font.bold = true;
          font.size = 24;
 
     text = new TextField();
     text.type = TextFieldType.INPUT;
     text.border = true;
     text.width = 250;
     text.height = 30;
     text.defaultTextFormat = font;
 
 
     var getDataBtn:LabelButton = new LabelButton();
          getDataBtn.label = "Get Tweets";
          getDataBtn.addEventListener(MouseEvent.CLICK,onGetDataClick);
 
     _verifyContainer.addChild(_spacer);
     _verifyContainer.addChild(label);
     _verifyContainer.addChild(text);
     _verifyContainer.addChild(getDataBtn);
     _verifyContainer.setSize(1024,600);
 
 
     removeChild(_authContainer);
     addChild(_verifyContainer);
 
     var authRequest:URLRequest = new URLRequest('http://api.twitter.com/oauth/authorize?oauth_token='+_requestToken.key);
     navigateToURL(authRequest);
}

Once the user comes back, puts in the PIN, and clicks the button, the application uses that information to build the request for the access token. By passing the PIN as the oauth_verifier property we get the access token we need to start requesting data from Twitter.

protected function onGetDataClick(event:MouseEvent):void
{
     var params:Object = new Object();
          params.oauth_verifier = text.text;
 
     _accessRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET,ACCESS_TOKEN_URL,params,_consumer,_requestToken);
 
     var accessUrlRequest:URLRequest = new URLRequest(_accessRequest.buildRequest(SIGNATURE));
     var accessLoader:URLLoader = new URLLoader(accessUrlRequest);
          accessLoader.addEventListener(Event.COMPLETE,onAccessRequestComplete);
}
 
protected function onAccessRequestComplete(event:Event):void
{
     _accessToken = getTokenFromResponse(event.currentTarget.data);
 
     var mainRequest:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_GET,API_URL+'/1/statuses/friends_timeline.xml',null,_consumer,_accessToken);
 
     var getStatusURLRequest:URLRequest = new URLRequest(mainRequest.buildRequest(SIGNATURE));
     var getStatusLoader:URLLoader = new URLLoader(getStatusURLRequest);
          getStatusLoader.addEventListener(Event.COMPLETE,onStatusLoadComplete);
}

Requesting data is pretty straightforward. Using the consumer and access tokens we just got we can build a normal request, send it off, and then parse the data that comes back. In this case I’m going to go through and display usernames and status for the tweets.

protected function onStatusLoadComplete(event:Event):void
{
     _mainContainer = new Container();
     _mainContainer.flow = ContainerFlow.HORIZONTAL;
 
     var sendTweetContainer:Container = new Container(25);
          sendTweetContainer.containment = Containment.DOCK_TOP;
 
     var font:TextFormat = new TextFormat();
          font.align = TextFormatAlign.CENTER;
          font.bold = true;
          font.size = 24;
 
     twitterTextField = new TextField();
     twitterTextField.type = TextFieldType.INPUT;
     twitterTextField.border = true;
     twitterTextField.width = 500;
     twitterTextField.height = 30;
     twitterTextField.defaultTextFormat = font;
 
     var tweetLabel:LabelButton = new LabelButton();
          tweetLabel.label = "Tweet This";
          tweetLabel.addEventListener(MouseEvent.CLICK,onTweetClick);
 
          sendTweetContainer.addChild(twitterTextField);
          sendTweetContainer.addChild(tweetLabel);
 
     // Code for parsing the XML from the response
     var xml:XML = new XML(event.currentTarget.data);
     var statusList:XMLList = xml.children();
     var arr:Array = new Array();
 
     for(var i:int=0;i<statusList.length();i++)
     {
          var obj:Object = new Object();
               obj.label = statusList[i].user.name.toString() +': ' + statusList[i].text.toString();
          arr.push(obj);
     }
 
     // Create the DataProvider out of the parsed data
     var dataProvider:DataProvider = new DataProvider(arr);
     var list:List = new List();
          list.dataProvider = dataProvider;
          list.size = 100;
          list.sizeUnit = SizeUnit.PERCENT;
          list.setSkin(AlternatingCellRenderer);
 
 
     _mainContainer.addChild(list);
     _mainContainer.addChild(sendTweetContainer);
     _mainContainer.setSize(1024,600);
     removeChild(_verifyContainer);
     addChild(_mainContainer);
}

The next step is to enable the ability to send a tweet. This one is a bit different. For all of the previous calls we just created a params object, and sent that with the OAuthRequest. But when we’re using POST instead of GET, things have to be done differently. Soenke Rohde has a Flash Twitter library that I used for help, but essentially we have to strip the URL params from the original request and then reset them as URLVariables to match the POST request.

protected function onTweetClick(event:MouseEvent):void
{
     var params:Object = new Object();
          params.status = twitterTextField.text;
 
     // Use the same consumer and accessToken to update the Status
     var tweetRequest:OAuthRequest = new OAuthRequest(OAuthRequest.HTTP_MEHTOD_POST,API_URL+'/1/statuses/update.json',params,_consumer,_accessToken);
 
     var setStatusURLRequest:URLRequest = new URLRequest(tweetRequest.buildRequest(SIGNATURE));
          setStatusURLRequest.method = URLRequestMethod.POST;
 
          // use the replace function to strip out the status
          setStatusURLRequest.url = setStatusURLRequest.url.replace("&status=" + URLEncoding.encode(params.status),"");
 
     // Add the status as a URLVariable since it's a POST operation
     setStatusURLRequest.data = new URLVariables( "status=" + twitterTextField.text  );
 
     var setStatusLoader:URLLoader = new URLLoader(setStatusURLRequest);
          setStatusLoader.addEventListener(Event.COMPLETE,onSetStatusComplete);
}

And that’s pretty much all there is to it. You can grab the whole bit of code over on Snipplr. Just swap in your own information from Twitter and you should be set to go.

One thing to keep in mind is that this example makes you authorize each time you use the application. In reality that would be very annoying so if you were using this in production you’d want to save the consumer and accessToken somewhere so you didn’t have to create those each time.

Tweetdeck One Year Later

There’s a good interview with Iain Dodsworth, the creator of TweetDeck, over on Louis Gray’s blog. Louis was one of the first people to discover and talk about TweetDeck and decided to chat with Iain a year later about how things are going. TweetDeck is still the Twitter client I use the most and provides me the most flexibility in searches and groups. During the interview there were a couple of AIR-related thoughts from Iain that I thought were worth sharing:

Louis: What made you decide to develop TweetDeck? You certainly went a different way with your product than others did, using the multi-column format, integrating Summize, groups, etc? What drove its initial feature set and had you choose the AIR platform?

Iain: …. AIR was an easy decision at the time – I had already been developing applications in Flex for financial institutions in London and there was no quicker way for a one man team to develop an application cross-platform.

Louis: TweetDeck, while popular, has also highlighted issues on Twitter’s end, especially around the service’s API limits. Also, the product has been a notorious memory hog and can take a good share of processing power. How are you working to reduce the demands taken on power users’ desktops, and how have you found working with Twitter and their API team, as they recently upped the API accesses users could hit per hour from 100 to 150?

Iain: I have worked very closely with Adobe to make improvements to the TweetDeck codebase and to work around various AIR/Flex issues. CPU & memory usage is an ongoing area for improvement and can sometimes be a bit of an art-form but we are getting there and the current version is a marked improvement over previous versions.

We’re always working (both on the actual runtime and with developers) to make sure that the AIR experience is better. I know the AIR team has been helped tremendously by Iain and all of the TweetDeck users. So congrats on a year Iain and thanks for helping make AIR a success.

Skimmer: The Most Well Designed AIR Application Yet?

skimmer_logoWhile at SXSW last week I got to chat with some of the folks from Fallon, a really great agency known for being on the leading edge of design and technology. They showed me an AIR application that proves that. The application is called Skimmer and is part Twitter client and part social manager. You can update your status for Twitter or Facebook but it also includes a media viewer which allows you to view your contact’s photos and videos from sites like Facebook, Flickr, and YouTube.

skimmer_shot

The key is that they spent a ton of time thinking about user experience and design to create the application and they said they wanted to capture a great out of browser experience for social media. To that end they’ve got very cool full screen functionality, drag and drop uploading to Flickr and YouTube as well as the option to view everything offline. It’s a perfect use-case for Adobe AIR – a bridge between the cloud and the desktop. One of my other favorite features is the widget creator. Once you sign into your accounts you can customize and generate a Flash-based widget that will show off your Flickr, Facebook, and Twitter posts. It’s something I plan to use on my root domain as a fun way to show what I’ve been doing.

On the technical side they also have a good implementation for the OAuth-type of functionality for Flickr and Facebook. This is something AIR developers are going to have to think about and I liked the way they implemented it with an extra window spawned by the application and loading the Facebook or Flickr authorization in an HTML control.

Congrats to the team at Fallon. I love this application.

Status is Useless Without Location

VentureBeat has a decent rundown of the news that Facebook is opening up their status APIs which prompted a lot of “ultimate social gesture. But so far the big players, FriendFeed, Twitter, and Facebook have failed because what’s missing is the geo aspect.

We’ve gotten to the point where people consider photos almost incomplete without some kind of geotagging. Most of the major sites support it and devices with cameras and GPSes, like the iPhone, support it by default. So why aren’t we there with status? Brightkite shows the value this can have. Take a look at a place and you can get a history of everything that’s happened there. But location associated with status is important because status is so real time. Don’t I also want to know what my friend is doing and where they’re doing it? (privacy controls enabled of course) Part of the brilliance of Twitter is that it’s so simple, but geotagging could be very easily done almost invisibly to the users who want to use it.

I think everything on Facebook should be “geoized” – I think they should buy Brightkite – because by adding the location dimension you open up new ways to advertise, new ways to visualize, and more importantly, new ways to connect. See what your friends had to say about where you are now, see what they’re doing right now and whether or not they’re close. Find out if there’s someone in your network at the same restaurant you’re in. Status providers need to embrace location for it to move to the next level.

What’s up with Twitter’s Flash/ActionScript 3 API?

Update: Brian found it – http://code.google.com/p/twitterscript/. But I still find it odd it isn’t listed on the library page. Maybe an oversight.

Chuck noticed that Twitter’s ActionScript3 Library is gone. They’ve moved their API stuff over to a Google Groups but AS3 no longer appears on the list of libraries. That’s odd considering how big AIR and the Flash Player have been for Twitter. I also noticed that they’ve locked down their crossdomain.xml file to a few companies. Is that the reason they ditched the AS3 API? Was it because of the changes in the Flash Player?

If anyone knows, I’d love to hear the story. Seems crazy that Twitter would just ditch AS3 like that.