PIN-Based Authentication in Java with Twitter4J

One of the lesser used authentication methods is PIN-based.  This is used in non-web applications such as GUI based apps or command line apps.  But even though it’s not used in in web apps, it still has a web component: the user has to visit the Twitter web site to authorize the application.  An overview of how it works is available on the Twitter developer’s site.

For our sample program, we use a Java command line program to get an OAuth access token.  Here’s the steps involved:

1. Initialization

The first steps are to initialize the Twitter object:

// Get the root twitter object
Twitter twitter = TwitterFactory.getSingleton();
// Set up the access tokens and keys to get permission to access
twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

 The CONSUMER_KEY and CONSUMER_SECRET are given to you from Twitter for your application.

2. Generate the Authorization URL

 Once you’ve created a Twitter object with your key/secret, you can ask Twitter for a request token; from the token you can get a URL to navigate the user to.


RequestToken requestToken = null;
   
try
{
  requestToken = twitter.getOAuthRequestToken();
}
catch (Exception e)
{
  System.out.println("Are the consumer key and secret correct?");
  e.printStackTrace();
}

The if your key/secret are invalid (or missing), or there is some network error, the call can throw an exception.  In production code better error handling (or at least more finesse in error handling) would be called for…

3. Send the user to the URL to authorize your application

 In a web world, you can just redirect the user to Twitter to complete the authorization flow. But in a non-web application, it’s not so simple.  There are basically two choices.  The first is to try to open a browser with the magic URL.  The second is to print the URL out and have the user copy and paste it into a browser.  The following code tries first to open a browser and, upon error, just prints the URL and instructs the user to copy/paste it. 

//	We're going to try to launch the browser for the user .... if we can
System.out.println("Launching browser...");
try
{
  Desktop desktop = Desktop.getDesktop();
  desktop.browse(new URI(requestToken.getAuthorizationURL()));
}
catch (Exception e)
{
  System.out.println("Problem in launching browser. Copy and paste the following URL into a browser:");
  System.out.println(requestToken.getAuthorizationURL());
}

 4. Get the PIN back from the user

The user, presumably, will visit the URL, authorize your application,  get a PIN code, and then copy the PIN code onto the clipboard.   Now it’s time to collect the PIN code from them and complete the authentication.  We’ll just read it in from the command line: 

System.out.print("Please enter the PIN from Twitter: ");

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String pin = null;

try
{
	pin = br.readLine();
}
catch (IOException e)
{
	System.out.println("I guess you don't want to?");
	e.printStackTrace();
	System.exit(-1);
}

AccessToken token  = null;
try
{
	token = twitter.getOAuthAccessToken(requestToken, pin);
}
catch (TwitterException e)
{
	System.out.println("Was there a typo in the PIN you entered?");
	e.printStackTrace();
	System.exit(-1);
}

twitter.setOAuthAccessToken(token);

 Given the original request token we got from our earlier call and the PIN the user typed (or pasted) in, we can generate an AccessToken.  An exception is thrown if the PIN is invalid or the requestToken is bad (e.g., it’s not the one you used to generate the URL).  The last line of the above code completes the process, and you are now free to issue API calls on behalf of the user.

In situations where you’d like to save the response for future use (so that you don’t have to keep bouncing the user to the authorization page), you will need to save the token key and secret.  For demo/debugging purposes, you can print them out:

System.out.printf("Token key=%s secret=%s\n", 
  token.getToken(), 
  token.getTokenSecret());

With the consumer key and secret and the token key and secret, you can re-authorize your application.  For example of how to do that, you can see the hard coded authorization example.

That’s all it takes!  For reference, below is a complete Java program that implements PIN based authorization and then does a simple query:

/*

	Twitter Authorization Example:	PIN Based Authorization

	When you have a non-web application (either command line or GUI), you cannot use the normal
	method of authorization (which routes the user to the authorization page on Twitter).  Instead,
	you have to have them go to a browser page on their own, authorize the app, and then copy and
	paste a PIN back that we can then use to get our credentials.

 */
package us.mcguinness;


import twitter4j.*;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

import java.awt.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;

public class Main {

	private static final String CONSUMER_KEY		= ""; // fill in with values from dev.twitter.com
	private static final String CONSUMER_SECRET 	= ""; // ditto

	public static void main(String[] args) {

		RequestToken requestToken = null;

		//	Get the root twitter object
		Twitter twitter = TwitterFactory.getSingleton();

		//	Set up the access tokens and keys to get permission to access
		twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

		try
		{
			requestToken = twitter.getOAuthRequestToken();
		}
		catch (Exception e)
		{
			System.out.println("Ah, well, there's an error with Twitter.  Are the consumer key and secret correct?");
			e.printStackTrace();
			System.exit(-1);
		}

		//	We're going to try to launch the browser for the user .... if we can
		System.out.println("Launching browser...");
		try
		{
			Desktop desktop = Desktop.getDesktop();

			//	Twitter creates a "magic" URL for us that contains a temporary token based upon our consumer info.
			//	The page at that URL will ask the user if they wish to authorize our application (as identified by
			//	the consumer key and secret).  If the user says yes, then a PIN code is generated and displayed
			//	to the user, who should copy it from the web page.
			desktop.browse(new URI(requestToken.getAuthorizationURL()));
		}
		catch (Exception e)
		{
			System.out.println("Problem in launching browser. Copy and paste the following URL into a browser:");
			System.out.println(requestToken.getAuthorizationURL());
		}

		System.out.print("Please enter the PIN from Twitter: ");

		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

		String pin = null;
		try
		{
			pin = br.readLine();
		}
		catch (IOException e)
		{
			System.out.println("I guess you don't want to?");
			e.printStackTrace();
			System.exit(-1);
		}

		AccessToken token  = null;
		try
		{
			token = twitter.getOAuthAccessToken(requestToken, pin);
		}
		catch (TwitterException e)
		{
			System.out.println("Was there a typo in the PIN you entered?");
			e.printStackTrace();
			System.exit(-1);
		}

		twitter.setOAuthAccessToken(token);

		System.out.printf("Token key=%s secret=%s\n", token.getToken(), token.getTokenSecret());

		//	Now do a simple search to show that the tokens work
		try
		{
			Query q = new Query("java programming");	// Search for tweets that contain these two words
			q.setCount(100);							// Let's get all the tweets we can in one call
			q.resultType("recent");						// Get all tweets
			q.setLang("en");							// English language tweets, please

			QueryResult r = twitter.search(q);			// Make the call

			for (Status s: r.getTweets())				// Loop through all the tweets...
			{
				System.out.printf("At %s, @%-20s said:  %s\n",
								  s.getCreatedAt().toString(),
								  s.getUser().getScreenName(),
								  s.getText());
			}

		}
		catch (TwitterException e)
		{
			System.out.println("That didn't work well...maybe the stack trace will tell us why?");
			e.printStackTrace();
		}

		//	That's all, folks!

	}
}

 

Leave a Reply