In previous post, we had built a simple register/login Android App using Firebase Email & Password authentication. Now we can let users authenticate with Firebase using Google Accounts by integrating Google Sign In into Kotin Android App.
Related articles:
– Kotlin Firebase Authentication – How to Sign Up, Sign In, Sign Out, Verify Email | Android
– Kotlin Firebase Authentication – Send Reset Password Email / Forgot Password | Android
Contents
I. Way to do
1. Add Firebase to Android App
– Read Kotlin Firebase Authentication – How to Sign Up, Sign In, Sign Out, Verify Email | Android and follow steps to enable and implement Firebase Auth in your Android App.
– Add dependency: build.gradle (App-level):
1 2 3 4 |
dependencies { // ... implementation 'com.google.android.gms:play-services-auth:11.0.4' } |
2. Enable Firebase Auth for Google Account
Go to Your Firebase Project Console -> Authentication -> SIGN-IN METHOD -> Enable Google:
3. Steps to authenticate with Firebase
3.1 Integrate Google Sign In into App
1- Add the Google Sign-In button:
1 2 3 4 |
<com.google.android.gms.common.SignInButton android:id="@+id/btn_sign_in" android:layout_width="wrap_content" android:layout_height="wrap_content"/> |
2- Configure Google Sign In object to request the user data:
When you configure the GoogleSignInOptions
object, call requestIdToken
:
1 2 3 4 5 6 7 8 |
override fun onCreate(savedInstanceState: Bundle?) { // ... val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(WEB_CLIENT_ID) .requestEmail() .build() } |
With WEB_CLIENT_ID
from Credentials page in the API Console -> OAuth 2.0 client IDs (Type Web Application).
3- Configure GoogleApiClient
object with access to the Google Sign In API and the options above:
1 2 3 4 5 6 7 8 9 10 |
private var mGoogleApiClient: GoogleApiClient? = null override fun onCreate(savedInstanceState: Bundle?) { // ... mGoogleApiClient = GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build() } |
3.2 signIn() method
1 2 3 4 |
private fun signIn() { val intent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient) startActivityForResult(intent, REQUEST_CODE_SIGN_IN) } |
In the activity’s onActivityResult
() method, we retrieve the sign-in result with Auth.GoogleSignInApi.getSignInResultFromIntent()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(); if (requestCode == REQUEST_CODE_SIGN_IN) { val result = Auth.GoogleSignInApi.getSignInResultFromIntent(data) if (result.isSuccess) { // successful -> authenticate with Firebase val account = result.signInAccount firebaseAuthWithGoogle(account!!) } else { // failed -> update UI updateUI(null) } } } |
3.3 Initialize the FirebaseAuth instance
In the Activity onCreate()
method:
1 2 3 4 5 6 |
private var mAuth: FirebaseAuth? = null override fun onCreate(savedInstanceState: Bundle?) { // ... mAuth = FirebaseAuth.getInstance() } |
3.4 Check if the user is currently signed in
In Activity onStart()
method:
1 2 3 4 5 6 |
override fun onStart() { super.onStart() // Check if user is signed in (non-null) and update UI accordingly. val currentUser = mAuth!!.currentUser updateUI(currentUser) } |
3.5 Authenticate with Firebase using the Firebase credential
For firebaseAuthWithGoogle()
method above:
After a user successfully signs in (in onActivityResult
), get an ID token from the GoogleSignInAccount
object, exchange it for a Firebase credential
, then authenticate with Firebase using the Firebase credential
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private fun firebaseAuthWithGoogle(acct: GoogleSignInAccount) { Log.e(TAG, "firebaseAuthWithGoogle():" + acct.id!!) val credential = GoogleAuthProvider.getCredential(acct.idToken, null) mAuth!!.signInWithCredential(credential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Sign in success val user = mAuth!!.currentUser updateUI(user) } else { // Sign in fails updateUI(null) } } } |
4. Sign Out User and Disconnect Account
4.1 Sign out User
1 2 3 4 5 6 |
private fun signOut() { // sign out Firebase mAuth!!.signOut() // sign out Google Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback { updateUI(null) } } |
4.2 Disconnect Account
1 2 3 4 5 6 |
private fun revokeAccess() { // sign out Firebase mAuth!!.signOut() // revoke access Google Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback { updateUI(null) } } |
II. Practice
1. Goal
We will build an Android App that can sign in/sign out, disconnect Google Account:
2. Technology
– Gradle 3.0.1
– Android Studio 3.x
– Firebase Android SDK 11.x
3. Project Structure
4. Step by step
4.1 Create Android Project & add Firebase Auth
– Generate new Android Project with package com.javasampleapproach.kotlin.authgoogle
.
– Follow these steps to add Firebase to Android Project.
– Don’t forget to enable Firebase Auth for Google Account:
– Add dependency: build.gradle (App-level):
1 2 3 4 |
dependencies { // ... implementation 'com.google.android.gms:play-services-auth:11.0.4' } |
4.2 Get server’s client ID
To find the OAuth 2.0 client ID:
– Open the Credentials page in the API Console.
– Select your Firebase Project.
– Scroll down to OAuth 2.0 client IDs, copy Client ID (Web Application Type).
4.3 Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
<?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/main_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#F5F5F5" android:orientation="vertical" android:weightSum="3"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:gravity="center_horizontal" android:orientation="vertical"> <TextView android:id="@+id/title_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:gravity="center" android:text="grokonez.com Google Sign In" android:textSize="30sp" /> <TextView android:id="@+id/tvStatus" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="4sp" android:text="Signed Out" android:textSize="18sp" /> <TextView android:id="@+id/tvDetail" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="4sp" android:textSize="18sp" tools:text="Firebase User ID: 123456789abc" /> </LinearLayout> <RelativeLayout android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" android:background="#E0E0E0"> <com.google.android.gms.common.SignInButton android:id="@+id/btn_sign_in" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:visibility="visible" tools:visibility="gone" /> <LinearLayout android:id="@+id/layout_sign_out_and_disconnect" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:orientation="horizontal" android:paddingLeft="16dp" android:paddingRight="16dp" android:visibility="gone" tools:visibility="visible"> <Button android:id="@+id/btn_sign_out" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" android:layout_weight="1" android:text="Sign Out" /> <Button android:id="@+id/btn_disconnect" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" android:layout_weight="1" android:text="Disconnect" /> </LinearLayout> </RelativeLayout> </LinearLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 |
package com.javasampleapproach.kotlin.authgoogle import android.support.v7.app.AppCompatActivity import android.os.Bundle import android.util.Log import android.view.View import android.widget.Toast import kotlinx.android.synthetic.main.activity_google_signin.* import com.google.android.gms.auth.api.Auth import com.google.android.gms.common.api.GoogleApiClient import com.google.firebase.auth.FirebaseAuth import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.android.gms.common.ConnectionResult import com.google.firebase.auth.FirebaseUser import android.content.Intent import com.google.android.gms.auth.api.signin.GoogleSignInAccount import com.google.firebase.auth.GoogleAuthProvider class GoogleSigninActivity : AppCompatActivity(), View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { private val TAG = "JSAGoogleSignIn" private val REQUEST_CODE_SIGN_IN = 1234 private val WEB_CLIENT_ID = "xxxxxx.apps.googleusercontent.com" private var mAuth: FirebaseAuth? = null private var mGoogleApiClient: GoogleApiClient? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_google_signin) btn_sign_in.setOnClickListener(this) btn_sign_out.setOnClickListener(this) btn_disconnect.setOnClickListener(this) val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken(WEB_CLIENT_ID) .requestEmail() .build() mGoogleApiClient = GoogleApiClient.Builder(this) .enableAutoManage(this /* FragmentActivity */, this /* OnConnectionFailedListener */) .addApi(Auth.GOOGLE_SIGN_IN_API, gso) .build() mAuth = FirebaseAuth.getInstance() } override fun onStart() { super.onStart() // Check if user is signed in (non-null) and update UI accordingly. val currentUser = mAuth!!.currentUser updateUI(currentUser) } override fun onClick(v: View?) { val i = v!!.id when (i) { R.id.btn_sign_in -> signIn() R.id.btn_sign_out -> signOut() R.id.btn_disconnect -> revokeAccess() } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(); if (requestCode == REQUEST_CODE_SIGN_IN) { val result = Auth.GoogleSignInApi.getSignInResultFromIntent(data) if (result.isSuccess) { // successful -> authenticate with Firebase val account = result.signInAccount firebaseAuthWithGoogle(account!!) } else { // failed -> update UI updateUI(null) Toast.makeText(applicationContext, "SignIn: failed!", Toast.LENGTH_SHORT).show() } } } private fun firebaseAuthWithGoogle(acct: GoogleSignInAccount) { Log.e(TAG, "firebaseAuthWithGoogle():" + acct.id!!) val credential = GoogleAuthProvider.getCredential(acct.idToken, null) mAuth!!.signInWithCredential(credential) .addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Sign in success Log.e(TAG, "signInWithCredential: Success!") val user = mAuth!!.currentUser updateUI(user) } else { // Sign in fails Log.w(TAG, "signInWithCredential: Failed!", task.exception) Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show() updateUI(null) } } } override fun onConnectionFailed(connectionResult: ConnectionResult) { Log.e(TAG, "onConnectionFailed():" + connectionResult); Toast.makeText(applicationContext, "Google Play Services error.", Toast.LENGTH_SHORT).show(); } private fun signIn() { val intent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient) startActivityForResult(intent, REQUEST_CODE_SIGN_IN) } private fun signOut() { // sign out Firebase mAuth!!.signOut() // sign out Google Auth.GoogleSignInApi.signOut(mGoogleApiClient).setResultCallback { updateUI(null) } } private fun revokeAccess() { // sign out Firebase mAuth!!.signOut() // revoke access Google Auth.GoogleSignInApi.revokeAccess(mGoogleApiClient).setResultCallback { updateUI(null) } } private fun updateUI(user: FirebaseUser?) { if (user != null) { tvStatus.text = "Google User email: " + user.email!! tvDetail.text = "Firebase User ID: " + user.uid btn_sign_in.visibility = View.GONE layout_sign_out_and_disconnect.visibility = View.VISIBLE } else { tvStatus.text = "Signed Out" tvDetail.text = null btn_sign_in.visibility = View.VISIBLE layout_sign_out_and_disconnect.visibility = View.GONE } } } |
WEB_CLIENT_ID
is the Client ID we get in the previous step.
4.4 Run & Check result
– Use Android Studio, build and Run your Android App.
– Firebase Console:
III. Source code
Last updated on July 13, 2018.
Thanks for the great example.
I am getting an error when using an emulator (pixel 2 27):
W/GooglePlayServicesUtil: Google Play services out of date. Requires 11910000 but found 11580470
Play services are installed on emulator and every seem to be up to date on Android Studio. Any ideas on what to try?
Hi Stephen Thoms,
It looks like you are trying to run using an API 27 emulator image.
Your options are to run using an API 26 emulator image, or downgrade the library versions in your build:
Regards,
JSA.
Thanks. Still had an issue ‘failing to log in’ but solved. See below.
Solved problem by using ‘ .requestIdToken(getString(R.string.default_web_client_id))’