sábado, 7 de abril de 2012

Android & Twitter - OAuth Authentication Tutorial

I’ve seen a lot of questions about connecting Android apps with Twitter using OAuth and thought I would write up a walkthrough of how it can be done. The example will be done using Twitter4J which is a great library for connecting Java apps with Twitter and provides a simple interface to connect to the public Twitter Web Services.

First thing you will need to do is to register your new app (even though you haven’t made it yet!). Do this by heading to https://dev.twitter.com/apps/new (you will need an existing Twitter account sign up). You should complete the form with the relevant details for your app:


You should fill in your details for the application such as name, description, URL – but the only key things to remember to do here are as follows:

1) Make sure you select “Browser” as your application type – even though we are creating an app, we will authenticate using the browser and a callback URL.

2) Tick read & write permissions

Once you have registered you will be provided with a “Consumer Key” and a “Consumer Secret” – make note of these for later – we will need to include these in the app.


Now let’s get started with the app! I will assume for this tutorial that you are familiar with the basics of Android apps so won’t go into details about how to setup the UI and what an Activity or Application is.
The first thing we will do is create an Activity that will prompt the user to login to Twitter and authorize the app to connect to Twitter to make updates. Let’s start with the onCreate method:


  1. @Override  
  2. public void onCreate(Bundle savedInstanceState) {  
  3.  System.setProperty("http.keepAlive""false");  
  4.  super.onCreate(savedInstanceState);  
  5.  setContentView(R.layout.main_oauth);  
  6.    
  7.  //check for saved log in details..  
  8.  checkForSavedLogin();  
  9.   
  10.  //set consumer and provider on teh Application service  
  11.  getConsumerProvider();  
  12.    
  13.  //Define login button and listener  
  14.  buttonLogin = (Button)findViewById(R.id.ButtonLogin);  
  15.  buttonLogin.setOnClickListener(new OnClickListener() {    
  16.   public void onClick(View v) {  
  17.    askOAuth();  
  18.   }  
  19.  });  
  20. }  

The first line we set the http keepAlive property – this is important so the app manages the connection correctly without reseting. The next important thing is our call to the checkForSavedLogin() method – this is a method that checks whether the user has previously authorised the app to access twitter, in which case we can just proceed as usual to the default twitter timeline activity (we will have a look at that method next). Otherwise we just initialise the Consumer/Provider and store them in our Application class so they are available to all Activities throughout the application. Then finally we inflate the button on the page and set a listener to call the askOAuth() method on click.


Now let’s jump in to the checkForSavedLogin() to see whats going on there:

  1. private void checkForSavedLogin() {  
  2.  // Get Access Token and persist it  
  3.  AccessToken a = getAccessToken();  
  4.  if (a==nullreturn//if there are no credentials stored then return to usual activity  
  5.   
  6.  // initialize Twitter4J  
  7.  twitter = new TwitterFactory().getInstance();  
  8.  twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);  
  9.  twitter.setOAuthAccessToken(a);  
  10.  ((TwitterApplication)getApplication()).setTwitter(twitter);  
  11.    
  12.  startFirstActivity();  
  13.  finish();  
  14. }  



This method calls a method getAccessToken() – all this does is check in the user SharedPreferences to see whether we have stored the users AccessToken previously – if there is no token found it just returns, so the user is left on the authentication activity waiting input. 

However, if a token is found, then we initialise Twitter4J Twitter object and store that in our Application class (again, so we can use it throughout the application).
Finally we call startFirstActivity(), which simply calls the default Twitter timeline activity we have and finishes the authentication activity as it is not needed.

So that is all good, if the user has previously authenticated then no probs – however, if no saved token is found the user still needs to authenticate so we need to handle the user action (remember we are calling the askOAuth() method from within our button listener). 



Let’s have a look at that method – this is where the authentication is all done:

  1. private void askOAuth() {  
  2.  try {  
  3.   consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);  
  4.   provider = new DefaultOAuthProvider("http://twitter.com/oauth/request_token""http://twitter.com/oauth/access_token""http://twitter.com/oauth/authorize");  
  5.   String authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);  
  6.   Toast.makeText(this"Please authorize this app!", Toast.LENGTH_LONG).show();  
  7.   setConsumerProvider();  
  8.   startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(authUrl)));  
  9.  } catch (Exception e) {  
  10.   Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();  
  11.  }  
  12. }  

First thing we do here is to initialise the Consumer and Provider objects – we are using the Commons OAuth Consumer/Provider (so you need to make sure you copy the following JARs in to your /assets/ directory and then add them to your build path: “signpost-commonshttp” and “signpost-core”).

You will see that the consumer is initialised using to static Strings CONSUMER_KEY and CONSUMER_SECRET – these need to be set to be the key and secret Twitter provided when you registered the app – you should make sure you keep these secret and not reveal them to people.

The provider is then just initialised using the relevant URLs that Twitter provides to authenticate againse.

Next, we call the retrieveRequestToken() method on the provider object, this takes two arguments: the consumer, and the CALLBACK_URL (another static string) we will come back to what this is later.

We then call startActivity with a new Intent using the authenticate URL – this is what will forward the user to the Twitter authentication service for them to securely login and authorize the app to make updates for them.



So now, we have a simple authentication activity, that if the user has never logged on before will forward them on to the Twitter OAuth service – next we need to handle the response from Twitter and if a successful response received, then store the Access Token for future use. This is all done in the onResume() method:

  1. @Override  
  2. protected void onResume() {  
  3.  super.onResume();  
  4.  if (this.getIntent()!=null && this.getIntent().getData()!=null){  
  5.   Uri uri = this.getIntent().getData();  
  6.   if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {  
  7.    String verifier = uri.getQueryParameter(oauth.signpost.OAuth.OAUTH_VERIFIER);  
  8.    try {  
  9.     // this will populate token and token_secret in consumer  
  10.     provider.retrieveAccessToken(consumer, verifier);  
  11.   
  12.     // Get Access Token and persist it  
  13.     AccessToken a = new AccessToken(consumer.getToken(), consumer.getTokenSecret());  
  14.     storeAccessToken(a);  
  15.   
  16.     // initialize Twitter4J  
  17.     twitter = new TwitterFactory().getInstance();  
  18.     twitter.setOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);  
  19.     twitter.setOAuthAccessToken(a);  
  20.     ((TwitterApplication)getApplication()).setTwitter(twitter);  
  21.     //Log.e("Login", "Twitter Initialised");  
  22.       
  23.     startFirstActivity();  
  24.   
  25.    } catch (Exception e) {  
  26.     //Log.e(APP, e.getMessage());  
  27.     e.printStackTrace();  
  28.     Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();  
  29.    }  
  30.   }  
  31.  }  
  32. }  


First thing, the method checks that the intent that has called the onResume is the expected intent, and then pulls out the verifying string from the callback URI – it then uses this string to retrieve the access token. Once we have called the retrieveAccessToken() method, we can initialise an AccessToken and store it. Next we initialise the Twitter4J object with the CONSUMER_KEY and SECRET again and then set the Access Token – once this has all been setup we just start the first activity as normal.
Once the user has authenticated they will be forwarded to the their friends timeline!

Now, the only thing remaining, is that we need to tell the app that once Twitter has finished authenticating that it needs to return to our Authentication Activity to call the above onResume method, or else it won’t be able to continue. To do this, Twitter will attempt to use the CALLBACK_URL we saw earlier. If you remember, when we requested the token from Twitter we passed in the CALLBACK_URL. I have previously defined this as follows:

  1. private String CALLBACK_URL =           "callback://tweeter";  

The name of the callback is up to you (I have named it “tweeter”), what is important is that this matches the callback you define in your Manifest XML. For the welcome activity (our Authentication activity) it needs to be defined as follows:

  1. <activity android:label="Tweeter" android:name=".AuthActivity">  
  2.  <intent-filter>  
  3.   <action android:name="android.intent.action.MAIN">  
  4.   <category android:name="android.intent.category.LAUNCHER">  
  5.  </category>  
  6.  <intent-filter>  
  7.   <action android:name="android.intent.action.VIEW">  
  8.   <category android:name="android.intent.category.DEFAULT">  
  9.   <category android:name="android.intent.category.BROWSABLE">  
  10.   <data android:host="tweeter" android:scheme="callback">  
  11.  </data>  
  12. </category>  
  13. </category></action></intent-filter></action></intent-filter></activity>  


As you can see, we have defined an intent-filter with a scheme called “callback” and we provide the host name as “tweeter” – matching that used to call the Twitter service.

And thats it! The user has been authenticated and returned to our application correctly, so it can now continue to read timelines, post tweets, retweet, etc.



The complete source code of the application is available here (NOW ON GITHUB!), please note that I have removed my own CONSUMER_KEY/SECRET details, so to make this work you will need to register your app and populate with your own details to see it work in your emulator/device.

1 comentario:

  1. i am getting error in askoauth method, the application is geting force stoped, thought i enter my consumer key and consumer secret key

    ResponderEliminar