Friday, July 15, 2011

Android OAuth with Google contacts tutorial with full source code

Hi Guys,
This is not a complete guide to OAuth with Google, The Best reference I found was here:
http://code.google.com/apis/accounts/docs/OAuth_ref.html


I spent some time with OAuth, but I didn't find any working example with complete source code, only partial stuff here and there, so I wrote this simple example, that authenticate with Google Contacts Service, it can be easly adapted to authenticate with any Google Service.
This example use the signpost library, that makes life easy, so you don't have to deal with the HTTP level.


So Here is what I do:

  1. I separated the application to 2 activities, this is a must, and I learned it the hard way, because for some reason Android will not start the main activity on the BROWSABLE intent filter, so there is a secondary activity that listen to the browser callback and act on it.

    
       
        
        
        
        
       
      
    
    


  2. When clicking on the launchOauth button, the RequestTokenActivity is started.

    launchOauth.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                 startActivity(new Intent().setClass(v.getContext(), RequestTokenActivity.class));
                 
                }
            });
    
    

  3. RequestTokenActivity  starts the OAuth dance, first initializing the consumer and provider, the calling the getRequestToken(), which starts the browser with the request token URL:

    private void getRequestToken() {
      try {
       Log.d(C.TAG, "getRequestToken() called");
       String url = provider.retrieveRequestToken(consumer, C.OAUTH_CALLBACK_URL);
       Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_FROM_BACKGROUND);
       this.startActivity(intent);
       
      } catch (Exception e) {
       Log.e(C.TAG, "Error retrieving request token", e);
      }
     }
    
    


  4. The browser lunched and redirected to Google accounts Authentication page, you can see that the application name "OAuth Example" is requesting permissions to access your contacts, the user should now click on the grant access button.






    You can also notice that there is a warning on the page that states that google can't verify the application, this happens because we use anonymous consumer key and secret (in C.java)

    public static final String CONSUMER_KEY  = "anonymous";
    public static final String CONSUMER_SECRET  = "anonymous";
    

    For production grade application get your consumer key and secret from Google.


  5. After the user clicked on the grant access, the browser callback is cached in the onNewIntent method (still in RequestTokenActivity),

    public void onNewIntent(Intent intent) {
      super.onNewIntent(intent); 
      prefs = PreferenceManager.getDefaultSharedPreferences(this);
      final Uri uri = intent.getData();
      if (uri != null && uri.getScheme().equals(C.OAUTH_CALLBACK_SCHEME)) {
       Log.i(C.TAG, "Callback received : " + uri);
       Log.i(C.TAG, "Retrieving Access Token");
       getAccessToken(uri);
      }
     }
    
    


    in this callback URL we have the Verifier parameter, the getAccessToken method will first extract this Verifier, that grants a short time temporary access to get the access token, then will use it to call the provider retrieveAccessToken method:

    final String oauth_verifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
    ... 
    provider.retrieveAccessToken(consumer, oauth_verifier);
    
    
    

    Finally we have the access token! the concludes the OAuth dance, we now store the token and the token secret in the SharedPreferences for future use, and go back to the OAuthMain activity.


  6. In the main activity we can now click on Get Contacts button, and see the list of contacts:


    The code that is relevant to OAuth is in the makeSecuredReq (in OAuthMain.java),
    Where is create the get contacts request and sign it using the consumer we create in the getConsumer Method (using the token and the secret we stored in the previous step).

    DefaultHttpClient httpclient = new DefaultHttpClient();
         HttpGet request = new HttpGet(url);
         consumer.sign(request);
         HttpResponse response = httpclient.execute(request);
    
    

    After we have the response we parse it using JSON (which is out of the scope of this tutorial, but is quite simple).



Thats it!

So Here is the Full Source Code of this example, hope you find it useful.

24 comments:

  1. Gr8 tutorial, code works great!

    ReplyDelete
  2. THX it is very clear ! :D AND HELPFULL can tel me how to i import Yahoo or hotmail contacts with it ? it is posibble to do such thing? or it is somthing else which is helpfuh :) THX

    ReplyDelete
  3. Hi Red,
    OAuth is open, but I'm not sure Yahoo or Hotmail implemented it on their side (google it), I know that Facebook and Twitter supports it (for login).

    ReplyDelete
  4. Hi John,
    I have followed this tutorial but it is giving me a error:

    "Could not find class 'oauth.signpost.commonshttp.CommonsHttpOAuthConsumer', referenced from method com.netcomps.oauth_example.RequestTokenActivity.onCreate".

    Please help me.

    ReplyDelete
    Replies
    1. rename the `lib` folder to `libs`

      Delete
    2. i am also getting the same error
      "Could not find class 'oauth.signpost.commonshttp.CommonsHttpOAuthConsumer', referenced from method com.netcomps.oauth_example.RequestTokenActivity.onCreate".


      tried lib to libs still getting the error ...

      Delete
    3. Right click the project & select properties, select Java Build Path, in that select Libraries tab & you will see two jar files with reference to old lib folder, so you have to remove those two jars & add new jar files i.e signpost-core & signpost-commonshttp from libs folder

      Delete
  5. How do i get details of other than account names

    ReplyDelete
  6. could you suggest me for gmail login connectivity in android

    ReplyDelete
  7. "Error retrieving request token" when execute.

    ReplyDelete
  8. I have downloaded your full source code and tried to execute but unfortunately got this error while clicking on button Do OAuth.

    E/OAuthExample(3655): oauth.signpost.exception.OAuthCommunicationException: Communication with the service provider failed: Service provider responded in error: 400 (Bad Request)

    ReplyDelete
  9. Same Error 12-22 java.lang.NoClassDefFoundError: oauth.signpost.commonshttp.CommonsHttpOAuthConsumer

    ReplyDelete
  10. This comment has been removed by the author.

    ReplyDelete
  11. There are few things you need to change to work around this error,

    1:
    Go to Project -> Properties, and under your Java Build Path -> Order and Export tab make sure those JARs are checked.

    2:
    in manifest file,
    change minSdkVersion to 8

    3:
    Go to Project -> Properties, and under Android -> set Project Build Target to Android 2.2

    4:
    You're done! Enjoy ;-)

    ReplyDelete
  12. Hey, It's working fine, but i am not getting all my contacts, can u give me the solution for this. Thanks

    ReplyDelete
  13. hi this is not working for android ICS can u please help how to do in ICS making error the following


    04-12 18:13:30.011: E/OAuthExample(1370): at oauth.signpost.AbstractOAuthProvider.retrieveToken(AbstractOAuthProvider.java:214)
    04-12 18:13:30.011: E/OAuthExample(1370): at oauth.signpost.AbstractOAuthProvider.retrieveRequestToken(AbstractOAuthProvider.java:69)
    04-12 18:13:30.011: E/OAuthExample(1370): at com.ntc.gmailutilities.RequestTokenActivity.getRequestToken(RequestTokenActivity.java:54)
    04-12 18:13:30.011: E/OAuthExample(1370): at com.ntc.gmailutilities.RequestTokenActivity.onCreate(RequestTokenActivity.java:34)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.Activity.performCreate(Activity.java:4465)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1920)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1981)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.ActivityThread.access$600(ActivityThread.java:123)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1147)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.os.Handler.dispatchMessage(Handler.java:99)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.os.Looper.loop(Looper.java:137)
    04-12 18:13:30.011: E/OAuthExample(1370): at android.app.ActivityThread.main(ActivityThread.java:4424)
    04-12 18:13:30.011: E/OAuthExample(1370): at java.lang.reflect.Method.invokeNative(Native Method)
    04-12 18:13:30.011: E/OAuthExample(1370): at java.lang.reflect.Method.invoke(Method.java:511)
    04-12 18:13:30.011: E/OAuthExample(1370): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
    04-12 18:13:30.011: E/OAuthExample(1370): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
    04-12 18:13:30.011: E/OAuthExample(1370): at dalvik.system.NativeStart.main(Native Method)
    04-12 18:13:30.011: E/OAuthExample(1370): Caused by: android.os.NetworkOnMainThreadException
    04-12 18:13:30.011: E/OAuthExample(1370): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1099)
    04-12 18:13:30.011: E/OAuthExample(1370): at java.net.InetAddress.lookupHostByName(InetAddress.java:391)
    04-12 18:13:30.011: E/OAuthExample(1370): at java.net.InetAddress.getAllByNameImpl(InetAddress.java:242)
    04-12 18:13:30.011: E/OAuthExample(1370): at java.net.InetAddress.getAllByName(InetAddress.java:220)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:137)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
    04-12 18:13:30.011: E/OAuthExample(1370): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
    04-12 18:13:30.011: E/OAuthExample(1370): at oauth.signpost.commonshttp.CommonsHttpOAuthProvider.sendRequest(CommonsHttpOAuthProvider.java:64)
    04-12 18:13:30.011: E/OAuthExample(1370): at oauth.signpost.AbstractOAuthProvider.retrieveToken(AbstractOAuthProvider.java:177)
    04-12 18:13:30.011: E/OAuthExample(1370): ... 17 more

    ReplyDelete
  14. not giving all contacts, solutions?

    ReplyDelete
    Replies
    1. change your lib folder into libs ..... and app will be work its working for me

      Delete
  15. change your lib folder into libs ..... and app will be work its working for me

    ReplyDelete
  16. Only 8 contacts will display.How can i get all contacts? Is there any solutions?

    ReplyDelete
  17. it's not working in minSdkVersion=9 or above.

    ReplyDelete
  18. all my contacts are not getting displayed and i need to display email address too.. wat should i add to the code.
    plz help me out..

    ReplyDelete