ZOFTINO.COM

Google Sign-In Option in Android Apps

To create google sign-in option in your app, first you need to configure google sign-in service in google developers console. To configure google sign-in service, you need SHA-1 hash of digital certificate that is used to sign your app’s apk.

SHA-1 Hash of Digital Certificate

If you don’t have digital certificate to sign your app, you can create self signed digital certificate using keytool utility available with JDK.

By default, android sdk creates debug keystore. For release, you need to create digital certificate.

Below is the command to create self signed certificate. You need to change keystore path, alias and validity. It prompts for key store password and private key password.

keytool -genkey -keyalg RSA -alias zoftinoapps -keystore C:\Project\android\myAppsKeyStore.jks -validity 20000 -keysize 2048 -v

Once self signed certificate is created, you can use below command to create SHA-1 hash of digital certificate.

keytool -exportcert -list -v -alias zoftinoapps -keystore C:\Project\android\myAppsKeyStore.jks

As mentioned, by default android sdk creates debug keystore and certificate, if you want to create google sign-in configuration for debug, you need to use below command to generate SHA-1 hash of debug digital certificate. It prompts for password, it is android.

keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore

Configure Google Sign-In Service for Your App

Now you have SHA-1 hash of digital certificate, let’s go ahead and create google sign-in service configuration. You need to login to google developers console and follow below steps to create google sign-in service configuration.

Select enable services for my android app

google sign-in service for apps

Create or choose an app and enter the package name which matches to your base package used in your project.

google sign-in service create app

Choose google sign-in service

google services configure services for app

Enter sha1 hash of your certificate created following above steps.

google sign-in service enter sha-1

Click generate configuration files

google sign-in service generate configuration

Download google sign-in service json configuration file

download google sign-in service json file

Add Google Sign-in Service Configuration File

Copy the downloaded google sign-in service json configuration file to your app project app/ folder.

Add Dependencies

You need to add google services and play services plugins and dependencies in your build.gradle.

Project level build.gradle file changes :

classpath 'com.google.gms:google-services:3.0.0'

App level build.gradle file changes :

apply plugin: 'com.google.gms.google-services'

compile 'com.google.android.gms:play-services-auth:10.2.0'

Create Google Sign-In Option

First google sign-in button needs to be displayed in UI by adding SignInButton in layout files.

google sign-in option in android app example
<com.google.android.gms.common.SignInButton
    android:id="@+id/g_sign_in"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="singInClick"></com.google.android.gms.common.SignInButton>

In onCreate() method of sign-in activity which handles on click event of SignInButton, initialize GoogleApiClient.

GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build();

googleApiClient = new GoogleApiClient.Builder(this)
        .enableAutoManage(this, connectionFailedListener)
        .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
        .build();

On click event handler of SignInButton creates inent by calling Auth.GoogleSignInApi.getSignInIntent()method which prompts user to enter credentials, performs authentication and sends result intent. In activity’s onActivityResult() method you can handle status and do other processing.

 Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
startActivityForResult(signInIntent, 22);

Sing Out and Revoke Access Options

Logged in users should be provided sing out and revoke access option. Sing out just logs the user out. Whereas with revoke access, account can be disconnected from app meaning user data received from google apis needs to be deleted from app.

google sign out and revoke access in android app example

Log out can be performed using Auth.GoogleSignInApi.signOut() method and an account can be disconnected by calling Auth.GoogleSignInApi.revokeAccess() method.

Authenticate with Server

Following above steps, you can setup process to authenticate user using google sing-in service on the device. If your app communicates with server and server needs to identify users, then you need to pass user id token to server.

Once server identifies users, client app can access user data on server and also server can access google api’s to get user data and provide expected app functionality to user.

It is important to use https to send user id token from app to server securely using https.

To authenticate with server, you need to create server client id, request server auth code, send auth token to server and verify it. All these steps are explained in detail below.

Create Server OAuth 2.0 Client Id

To get client id, you need to login to google developers console.

Once you login, go to OAuth 2.0 client IDs section in credentials tab and find web application client id as shown in below image.

google developers console server oauth 2.0 client id

Request Server Auth Code

When you initialize GoogleApiClient in your activity you can pass GoogleSignInOptions to it. If you need to authenticate user on server, you need to pass server client id obtained by following above steps.

 String serverClientId = getString(R.string.server_oauth_client_id);
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestIdToken(serverClientId )
        .build(); 

Send Auth Code to Server and Verify Token

Once authentication is done in your app, you can retrieve server auth token in onActivityResult method and send it to server using https post.

Retrieve auth code

            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);

            if (result.isSuccess()) {
                GoogleSignInAccount acct = result.getSignInAccount();
                String idToken =acct.getIdToken();
	    }

Send auth code to server

HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("https://yourappserverbackendexample.com/clientsignin");

try {
    List prams = new ArrayList(1);
    prams.add(new BasicNameValuePair("authToken", authToken));
    httpPost.setEntity(new UrlEncodedFormEntity(prams));

    HttpResponse response = httpClient.execute(httpPost);
    int statusCode = response.getStatusLine().getStatusCode();   
    Log.i("Server Authentication", "server auth token response status : " + statusCode);
} catch (Exception e) {
    Log.e("Server Authentication", "Exception sending server auth token", e);
}

Vlidating Server Auth Token on Server

On server, you need to validate the server auth token received from your client app. Auth token validation can be done using google api client library.

import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.android.gms.auth.api.credentials.IdentityProviders;

....
    
private static final HttpTransport transport = new NetHttpTransport();
private static final JsonFactory jsonFactory = new JacksonFactory();

....

    GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
                    .setIssuer(IdentityProviders.GOOGLE)
    		.setAudience(Collections.singletonList("client id come up with string to identify this client"))
    		.build(); 


	try {
       
            GoogleIdToken idToken = verifier.verify(authTokenRecievedFromAppClient);
            if (idToken != null) {
                //auth token verified and valid
           
            }

        } catch (Exception e) { //error validating auth token }

Issue Noticed

While I was testing google services sing-in on emulator, I noticed below message printing continuously in logs.

03-20 17:19:29.767 2455-2455/com.google.android.gms.persistent I/GCoreUlr: Place inference reporting – stop

If you notice similar issue, you need to force stop google play store services app and google paly store app and clean the cache. You can do so by going to settings > apps and clicking those apps.

Google Sign-In Option in Android Apps Example

Activity

package com.zoftino.content;


import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import com.google.android.gms.auth.api.Auth;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
import com.google.android.gms.auth.api.signin.GoogleSignInResult;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.SignInButton;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;

public class GoogleSingInActivity extends AppCompatActivity {

    private GoogleApiClient googleApiClient;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_google_signin);

        GoogleSignInOptions googleSignInOptions = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestEmail()
                .build();

        googleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this, connectionFailedListener)
                .addApi(Auth.GOOGLE_SIGN_IN_API, googleSignInOptions)
                .build();

        SignInButton signInButton = (SignInButton)findViewById(R.id.g_sign_in);
        signInButton.setOnClickListener(onClickListener);
        signInButton.setSize(SignInButton.SIZE_WIDE);

    }

    public void singInClick() {
        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(googleApiClient);
        startActivityForResult(signInIntent, 22);
    }
    public void signOutClick(View v) {
        Auth.GoogleSignInApi.signOut(googleApiClient).setResultCallback(
                new ResultCallback<Status>() {
                    @Override
                    public void onResult(Status status) {
                        findViewById(R.id.sign_out).setVisibility(View.GONE);
                        findViewById(R.id.g_sign_in).setVisibility(View.VISIBLE);
                        findViewById(R.id.revoke_access).setVisibility(View.GONE);
                    }
                });
    }
    public void revokeAccessClick(View v) {
        Auth.GoogleSignInApi.revokeAccess(googleApiClient).setResultCallback(
                new ResultCallback<Status>() {
                    @Override
                    public void onResult(Status status) {
                        findViewById(R.id.sign_out).setVisibility(View.GONE);
                        findViewById(R.id.revoke_access).setVisibility(View.GONE);
                        findViewById(R.id.g_sign_in).setVisibility(View.VISIBLE);
                    }
                });
    }
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == 22) {
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);

            if (result.isSuccess()) {
                GoogleSignInAccount acct = result.getSignInAccount();

                findViewById(R.id.g_sign_in).setVisibility(View.GONE);
                findViewById(R.id.sign_out).setVisibility(View.VISIBLE);
                findViewById(R.id.revoke_access).setVisibility(View.VISIBLE);
            } else {
                findViewById(R.id.sign_out).setVisibility(View.GONE);
                findViewById(R.id.revoke_access).setVisibility(View.GONE);
                findViewById(R.id.g_sign_in).setVisibility(View.VISIBLE);
            }
        }
    }

    GoogleApiClient.OnConnectionFailedListener connectionFailedListener = new GoogleApiClient.OnConnectionFailedListener() {
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {
            Log.d("google sign-in", "connection failed " + connectionResult);
        }
    };
    View.OnClickListener onClickListener = new View.OnClickListener(){
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.g_sign_in:
                    singInClick();
                    break;
            }
        }
    };
}

Activity Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_gsignin"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.zoftino.content.GoogleSingInActivity">
    <com.google.android.gms.common.SignInButton
        android:id="@+id/g_sign_in"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="singInClick"></com.google.android.gms.common.SignInButton>
    <Button
        android:id="@+id/sign_out"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:visibility="gone"
        android:onClick="signOutClick"
        android:text="Sign Out"></Button>
    <Button
        android:id="@+id/revoke_access"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:visibility="gone"
        android:onClick="revokeAccessClick"
        android:text="Revoke Access"></Button>
</LinearLayout>