Two-Minute Tutorial 5 Part 1

TMT5 Twitter: jsOAuth and Child Browser plugin: non-PIN OAuth access!
This advanced Two-Minute Tutorial (TMT5) shows how to use the Twitter REST API in your AppLaud app. Using the PhoneGap ChildBrowser for Android plugin and jsOAuth JavaScript library, non-PIN OAuth 1.0a authentication is demonstrated. After authentication, the app has access to the Twitter REST API. The tutorial app uses home timelines, mentions and tweets as examples of what's possible, right from the app's simple, intuitive jQuery Mobile UI. The oauth access tokens are saved in local storage on the device, enabling continuous access to twitter.

Sample App Summary


When the user initiates authorization, the app presents the Twitter authorization page in a child browser. Upon successful Twitter log in and grant of authorization, the Twitter REST API becomes available to the app. Making use of open-source, extremely helpful plugin and library, the app gets OAuth 1.0a authorization with Twitter, without requiring a PIN number. Using oauth's callback url method (i.e. not oob or PIN method), the app detects when the browser is loading the callback url, and intervenes by directing control back to the native app.


Prerequisites
  • Installation of the latest MDS AppLaud plugin, see Get Started
  • Complete of TMT0 or equivalent (create project, edit files, run app on device or AVD)
  • Twitter Account and Twitter Developer Sign-in
    • Tweet something, follow someone, try to get mentioned!
Prep
  • Download the attached zip file tmt5p1.zip
    • src/ directory contains:
      • index.html - all html and jQuery Mobile, project creation will add scripts
      • main.js - complete javascript for app
      • jsOAuth-1.3.1.js - jsOAuth library (see Step 2 below for more info)
  • Download the corresponding version of PhoneGap Plugins from github:
    • PhoneGap / Cordova 1.5.0 - 1.8.1:
    • PhoneGap / Cordova 1.9.0: Not Supported
    • PhoneGap / Cordova 2.0+ (e.g. your downloaded version):
  • NEW: Watch the Video: How to Install a PhoneGap Plugin in AppLaud
  • Create a Twitter Application at https://dev.twitter.com/apps including the following:
    • Read and write access permissions
      • Note: this tutorial does not access direct messages so "Access direct messages" permission is not needed. See note Direct Messages for more information.
    • Organization or personal website in Application Details
    • Organization or personal website for callback url in Application Type
      • Do not leave callback url blank, Do not enter 'oob'
    • Note: Use of callback url is non-standard. More explanation below.
  • AVD users: Create AVD with SD Card
Resources

Project Creation and Plugin Addition

1. Create a new AppLaud project
 
Using the project creation wizard, create a new project that includes:
  • Built-In PhoneGap
  • jQuery Mobile Libraries
  • Project Contents: locate the /src directory (see Prep step above)
Finish creating the project in the second window: fill in project name, etc. Upon completion the new project's /assets/www/ directory should appear exactly as below (modulo PhoneGap version).

2. jsOAuth JavaScript Library

The jsOauth javascript library was written by Rob Griffiths. From the jsOAuth library readme: "jsOAuth is a javascript library implementing the OAuth protocol. jsOAuth aims to form the basis of custom clients such as Twitter and Yahoo."

Using Rob's library in the app for Twitter's OAuth 1.0a authorization makes the oauth process seem much simpler than it really is, and for that we thank him heartily. Take a look at the library code, then at the simplified API for jsOAuth's GET and POST functions. With jsOAuth library, many of the non-pretty oauth details are kept separate from app code.

By creating the
AppLaud project as described above, the jsOAuth-1.3.1.js file is automatically pulled into the assets/www/ directory.

Alternative: Instead of using the jsOAuth library from the attached zip, download the complete package from https://github.com/bytespider/jsOAuth and build it. Import the javascript library into
assets/www/. Currently Eclipse can not parse the minified version without errors, therefore you must rename the min version to be something like "jsOAuth-1.3.1.min" (do not use .js extension).

Include the following line in index.html
before main.js:

      <script type="text/javascript" src="jsOAuth-1.3.1.js"></script>

The jsOAuth library is now accessible from main.js.

3. PhoneGap ChildBrowser Plugin

New: For an overview of PhoneGap Plugins, and specific instructions on installing the ChildBrowser Plugin for Android, watch our new video: How to Install a PhoneGap Plugin for Android in AppLaud.

See the ChildBrowser plugin README.md file in the package downloaded during prep (also here). The section Adding the Plugin to your Project describes installation. The following changes to the 5 instructions are specific to AppLaud projects:

1.
To install the plugin, move www/childbrowser.js to your project's /assets/www folder and include a reference to it in your html file after phonegap-1.4.1.js. Add the following line to index.html:

<script type="text/javascript" charset="utf-8" src="childbrowser.js"></script>

AppLaud project wizard will add phonegap-1.4.1.js for you in a comment; this must be uncommented. Make sure your final collection of links and scripts is as follows, order of files is important:

      <link rel="stylesheet" href="jquery.mobile/jquery.mobile-1.0.1.css" type="text/css">
      <script type="text/javascript" src="jquery.mobile/jquery-1.6.4.min"></script>
      <!-- The following must be AFTER jquery core and BEFORE jquery mobile js -->
      <script>
        $(document).bind("mobileinit", function(){
              $.mobile.touchOverflowEnabled = true;
        });
      </script>
      <script type="text/javascript" src="jquery.mobile/jquery.mobile-1.0.1.js"></script>
      <script type="text/javascript" src="jsOAuth-1.3.1.js"></script>
      <script type="text/javascript" charset="utf-8" src="phonegap-1.4.1.js"></script>
      <script type="text/javascript" charset="utf-8" src="childbrowser.js"></script>
      <script type="text/javascript" charset="utf-8" src="main.js"></script>


2. No changes: Copy the image files folder www/childbrowser to your project's www folder. Note you need the entire folder not just the images.

3.  To add a java package and source to an AppLaud project:

In the new project, right click on the top level /src directory and select New -> Package to bring up the New Java Package creation window. In the example below, the project name is tmt5p1 so the Source folder is tmt5p1/src. Enter the package name: com.phonegap.plugins.childBrowser as shown below. This package name was specified in the plugin's README.md and must match exactly. Click Finish.
Eclipse's Java support creates the directory structure needed to support java packages, based on the name entered here.

Add the plugin's java file to the project. Open the /src directory in the project and right click on the new java package
com.phonegap.plugins.childBrowser. Select Import... and navigate to the directory on your host where ChildBrowser.java is located. Select ChildBrowser.java to be imported, as shown below.

Note: To edit or extend java code, just edit file. Now you can take advantage of AppLaud's hybrid Java/JavaScript capabilities to extend the API, change messages, or make any other changes to the Java file. The resulting files will be automatically built into your project.

When the java files has been successfully added to the project, the Project Explorer src/ directory and new package will appear as below.


4. When editing plugins.xml in AppLaud, click on the bottom tab titled Source, to edit the raw text:

res/xml/plugins.xml file add the following line:

<plugin name="ChildBrowser" value="com.phonegap.plugins.childBrowser.ChildBrowser"/>
4. Add Twitter App-Specific Information

The Prep step regarding Twitter App registration provides you with the information you need for this section. Near the top of main.js locate the following declaration and url refs as described below:
    var options = {
            consumerKey: 'YOUR-CONSUMER-KEY',
            consumerSecret: 'YOUR-CONSUMER-SECRET',
            callbackUrl: 'http://www.your-callback-url.com' };

Refer to your app’s home page on twitter, under OAuth Settings, to find these values:
  • Consumer key - replace YOUR-CONSUMER-KEY
  • Consumer secret - replace YOUR-CONSUMER-SECRET
  • Callback url - replace with app's callback url (3 places)
    • callbackUrl: 'http://www.your-callback-url.com'
    • if (loc.indexOf("http://your-callback-url.com/?denied") >= 0)
    • if (loc.indexOf("http://www.your-callback-url.com/?") >= 0)
  • App's homepage - This must match the homepage given on the app's Twitter page
    • if (loc === "http://www.your-app-homepage-url.com/")
The options object is passed into the OAuth() init function, where oauth session-specific values are stored, encoded and used as needed during oauth authorization. More steps and data are used during oauth process, all of which the jsOAuth library handles silently.

Locate the var localStoreKey near the top of main.js. This holds the localStorage key string used to save access tokens. Change the string "tmt5p1" to your own unique string.

5. Review of Code

OAuth Process Without PIN Verification - A brief review, provided in order of execution, not order in the source file. Search and review all calls using the oauth object in this app (oauth.get, oauth.post, etc).


The oauth object


oauth = OAuth(options);

Create and initialize the oauth object as shown above at the start of each oauth session. When acquired, access token will be saved in the oauth object (courtesy of jsOAuth library) and used in subsequent requests. In the case where some action fails, or the user cancels the session and restarts, new access tokens and other data will be needed. Recreating the object at the start of each new oauth session prevents using stale data.

Retrieve a request token

oauth.get('https://api.twitter.com/oauth/request_token',
        function(data) {
            requestParams = data.text;
            $('#oauthStatus').html('<span style="color:blue;">Getting authorization...</span>');
            window.plugins.childBrowser.showWebPage('https://api.twitter.com/oauth/authorize?'+data.text,
                    { showLocationBar : false });                   
        },
        function(data) {
            alert('Error : No Authorization');
            $('#oauthStatus').html('<span style="color:red;">Error during authorization</span>');
        }
);

A standard GET of a request token, using oauth.get(). The first param is the twitter url for request tokens. The second param, the success handler, gets the requestParams
from the data returned by twitter (in data.text), and uses it in the next step: user login / authorization in a child browser window.


Get user authorization by opening the twitter authorization page (
https://api.twitter.com/oauth/authorize), including the oauth_token and oauth_token_secret values as params (data.text). Use the  showWebPage() option { showLocationBar : false } as there is no need for the location bar.

This step will open a child browser window as shown. The twitter authorization page shows the app "AppLaud Two-Minute Tutorial 5" - your app name and icon should appear in your version. Log in with your twitter screen name and password. After the initial log in, your name and password will be cached. To log in with a different user name select "Logout" from the upper right corner near the profile picture (not shown).

Exchange request token for access token

If the user successfully logs in and grants authorization, the browser will be directed to load the provided oauth_callback_url (the one given in step 4 above). This is where the usefulness of the new onLocationChange() callback shines brightly. Because we provide the onLocationChange() callback function, we are notified of every location change, meaning the url and url param data returned from twitter. We check for our url (the oauth_callback_url) and intercept its loading.

The first two if’s check for the url and params used when the user declines authorization. In the first case, the user returned to the app (param "denied" appended to callback url); in the second case, to the app’s homepage (in this example, coincidentally the same as oauth_callback_url). Note that the callback url provided by the app does not need to match the url given on the App’s home page.

if (typeof window.plugins.childBrowser.onLocationChange !== "function") {
    window.plugins.childBrowser.onLocationChange = function(loc){
 
        // If user hit "No, thanks" when asked to authorize access
        if (loc.indexOf("http://www.your-callback-url.com/?denied") >= 0) {
            $('#oauthStatus').html('<span style="color:red;">User declined access</span>');
            window.plugins.childBrowser.close();
            return;
        }

       // Same as above, but user went to app's homepage instead
       // of back to app. Don't close the browser in this case.
       if (loc === "http://www.your-app-homepage-url.com/") {
            $('#oauthStatus').html('<span style="color:red;">User declined access</span>');
                return;
       }


The third and final if checks if the url being loaded (our callback url) contains the verifier needed to get an access token. The oauth_verifier value is extracted and sent back in another oauth.get() call, this time requesting an access token. The success handler for this call will then extract the access token and secret, and save them in the oauth object for future use. The access token key and secret are also stored in localStorage.

Finally, the child browser is closed using the plugin's .close() method. Pseudo code shown below for brevity.

// The supplied oauth_callback_url for this session is being loaded
if (loc.indexOf("http://www.your-callback-url.com/?") >= 0) {
     // EXTRACT VERIFIER

     // Exchange request token for access token

     oauth.get('https://api.twitter.com/oauth/access_token?oauth_verifier='+verifier+'&'+requestParams,
          // SUCCESS HANDLER: EXTRACT ACCESS TOKEN KEY and SECRET
          // SAVE TOKEN KEY/SECRET in oauth obj

          // SAVE TOKEN KEY/SECRET in localStorage
          // CALL oauth.get() TO GET USER'S screen_name
          window.plugins.childBrowser.close();
     },
          // FAIL HANDLER: REPORT FAILURE,
     }
);

Use of localStorage

To review the use of device localStorage, track all locations of "localStorage." in main.js.

Upon doc load, attempt to get the item stored with key localStoreKey ("tmt5p1" in tutorial, change to your unique string), the key used to store the oauth access token key/secret upon successful authorization. If a value is found, use in a credentials verification request to 1)
verify them, and 2) get the screen_name. If the credentials verification request succeeds, present the screen name to the user in a dialog to confirm (see screenshot at left). If the user continues, skip the authorization step and go to the twitter api screen. If the user does not continue with the current screen name (i.e. stored access tokens), oauth verification is started from the beginning.

Saving and reusing the access token allows continuous use of twitter without requiring the user to log in and authenticate each time. Consider the security implications of storing access tokens.

Stored tokens are deleted upon explicit cancellation of the session (see Cancel below) or when found to be invalid.

Home Timeline

oauth.get('https://api.twitter.com/1/statuses/home_timeline.json?count=10', success, fail);

The home timeline is what a twitter user sees on their twitter home page. This is a GET, so oath.get() is used. The parameter count is shown for an example of how to request a maximum number of statuses. The app displays 1 status for brevity, and prepends it to the div twitterdata
(see index.html).

Mentions

oauth.get('https://api.twitter.com/1/statuses/mentions.json' + mentionsParams, success, fail);

Also a GET..  An example of using params with mentions uses the since_id parameter. The last mention id (i.e. timestamp) is saved each time for use in future mention requests, so they will return only mentions newer than the current one..


Tweet!

oauth.post('https://api.twitter.com/1/statuses/update.json',
                        { 'status' : theTweet,  // jsOAuth encodes for us
                          'trim_user' : 'true' }, success, fail);

This is a POST, so use oauth.post(). The jsOAuth API requires a key/value object to indicate the request's parameters. The ‘status’ is required (also known as the 'tweet'). The parameter trim_user indicates we don’t need extra user data returned to us.
As a courtesy, this app pops a dialog forcing the user to confirm the tweet before posting.

Cancel

oauth.post('http://api.twitter.com/1/account/end_session.json', success, fail);

Use to officially end session and useful for debugging your app.
From official Twitter documentation regarding the status of the oauth Twitter session over time:

         "You can use an access token until the member severs the connection."

Refer to the official Twitter REST API for a complete list of APIs and their parameters.


















Twitter Direct Messages

This tutorial does not attempt to use the Direct Messages part of Twitter's REST API. To do so, change the access permission level of your app to include "Access Direct Messages" in addition to Read and Write.
 

Had trouble with the above or have comments? Just want to talk about using Twitter in apps? Join us on the mailing list or create an issue.

Return to the main tutorial page.
Č
ċ
ď
Elizabeth Baldwin,
Nov 10, 2011, 2:04 PM
ċ
ď
Elizabeth Baldwin,
Nov 10, 2011, 2:05 PM
Comments