A beginner's guide to OAuth 2.0 with Google sign-in: Enhance Android app authentication
Tags: appwrite, oauth, android, jetpackcompose
Published: 2023-08-04 07:57:50

Android mobile operating system is the most popular operating system in the world, with approximately [2.3 Billion](https://www.statista.com/topics/876/android/) users and a market share of 71.4%. It has even seen further adoption due to the recent introduction of Jetpack Compose, an Android modern toolkit for building native UI with less code and powerful Kotlin API. Developers can now build native and cross-platform applications for phones, TVs, computers, etc.

Beyond building these applications, developers must also ensure that they are secure and that only authorized users can access protected resources.

In this post, we will follow a step-by-step guide on enhancing an Android app authentication using Google as the provider. The project repository can be found [here](https://github.com/Mr-Malomz/android_auth).

## Prerequisites

To fully grasp the concepts presented in this tutorial, the following are required:

-   Basic understanding of Kotlin and Android
-   IntelliJ IDEA or Android Studio properly set up
-   Appwrite account [s](https://cloud.appwrite.io/register)[ign-up](https://cloud.appwrite.io/register) is completely free
-   Google [account](https://console.cloud.google.com/)

## Getting started

To get started, we need to clone the project by navigating to the desired directory and running the command below:

```bash
git clone https://github.com/Mr-Malomz/android_auth.git
```

The project's `com.example.android_auth` directory consists of the following packages:

-   `navigation` consists of files used in setting up the application navigation
-   `screens` consist of the application UI screens

## Running the project

We can run the project by selecting the preferred emulator and clicking the **Run** button.

![Run app](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688080193212_Screenshot+2023-06-30+at+00.09.28.png)

The step above will run the application on the selected device.

![Login screen](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688680375780_Screenshot_20230706_225011.png)
![Home screen](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688680387269_Screenshot_20230706_225234.png)

## Set up a project on Appwrite

To get started, we need to log into our Appwrite console, click the **Create project** button, input `android_auth` as the name, and **Create**.

![Create project](https://paper-attachments.dropboxusercontent.com/s_C9210607DAFF6D80615947ED6BE237716E98BADEF0416B60FACF081B1CAEFB80_1687518026974_Screenshot+2023-06-23+at+11.38.55.png)

**Add platform support**

To add support for our Android app, navigate to the **Home** menu and click the **Android App** button.

![Add platform](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688080751046_Screenshot+2023-06-30+at+00.18.22.png)

Next, we must modify the application by inputting `android_auth` as the **Name** and `com.example.android_auth` as the **Package Name**.

![Add platform support](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688080892297_Screenshot+2023-06-30+at+00.20.58.png)

**Enable Google as the preferred OAuth 2.0 provider**

To do this, we must navigate to the **Auth** menu, click the **Settings** tab, and enable `Auth0` as a provider.

![Navigate to the provider](https://paper-attachments.dropboxusercontent.com/s_A947A84E466F3A1129628A6A175CCC5CBAF61907861234B6B59949686454755F_1687616251008_Screenshot+2023-06-23+at+21.53.41.png)
![Google OAuth](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688116703823_Screenshot+2023-06-30+at+10.17.42.png)

We will fill in the required details later. However, we must copy and keep the redirect **URI**, it will come in handy when setting up our application on the Android developer portal.

![Copy URI](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688116782258_Screenshot+2023-06-30+at+10.19.12.png)

## Set up Google OAuth 2.0

With our project partly configured on Appwrite, we must also set up our project on the Google console.

**Create an OAuth consent screen**

The OAuth consent screen helps us configure the application scope, policy, and other relevant details required for OAuth 2.0 implementation. It also shows our users a summary of the project and the kind of permissions our application requires. To do this, we need to log into the Google console and create a project.

![Create project](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688118424695_Screenshot+2023-06-30+at+10.45.13.png)

Navigate to the **OAuth consent screen** under **APIs & Services**, select **External**, and **Create**.

![navigate to OAuth consent screen](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688119108434_Screenshot+2023-06-15+at+22.50.59.png)
![Select external and create](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688119108405_Screenshot+2023-06-15+at+22.52.13.png)

Input `Quickstart` as the App name, fill in other required details, and **Save and Continue**.

![App info](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688120098648_Screenshot+2023-06-15+at+23.12.30.png)

**PS**: _We must name our application_ `Quickstart` _to avoid the “_[_An error occurred while saving your app_](https://www.google.com/search?q=An+error+occurred+while+saving+your+app&rlz=1C5CHFA_enNG931NG931&oq=An+error+occurred+while+saving+your+app&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIICAEQABgWGB7SAQc2MTdqMGo0qAIAsAIA&sourceid=chrome&ie=UTF-8)” _error._

Click on the **Add or Remove Scopes**, add `email` and `profile` as shown below and **Update**.

![Add scope](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688120599619_Screenshot+2023-06-15+at+23.13.14.png)

Next, we need to add test users. Only the users we registered here will be able to test our application. To do this, click the **+ Add User** button, input the test user’s emails (Gmail only) and select **Add**.

![Add test users.](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688121050136_Screenshot+2023-06-30+at+11.29.38.png)

Then click the **Save and Continue** button and review the configuration.

**Create Credential**

Credentials on Google console help applications securely access Google’s enabled services. We must create a credential for our application to use the OAuth 2.0 functionality. To do this, navigate to the **Credentials** tab, click the **+ Create Credentials** button and select the **OAuth client ID**.

![Navigate](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688121417773_Screenshot+2023-06-15+at+23.08.31.png)
![Select OAuth](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688121417739_Screenshot+2023-06-15+at+23.09.31.png)

Select `Web application` as the **Application type**, input `android_auth` as the **Name**, input the **Redirect URI** we copied from Appwrite earlier as the **Authorised** **redirect URIs**, and **Create**.

![](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688122707002_Screenshot+2023-06-30+at+11.58.16.png)

On creation, we should see our application **Client ID** and **Client Secret**. We need to copy and keep these credentials as they will be useful when finishing up our application set-up on Appwrite.

![ID and Secret](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688123521071_Screenshot+2023-06-30+at+12.10.51.png)

## Putting it together on Appwrite

With our application configured on Google, we must create a link to it on Appwrite. To do this, we must update the Google provider details with the copied **Client ID**, **Client secret**, and **Update**.

![Fill and Update](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688123814138_Screenshot+2023-06-30+at+10.17.42.png)

## Integrating Google OAuth 2.0 with Android using Appwrite

With all that done, let’s build OAuth 2.0 into our application. First, we need to install the required dependency. To do this, we need to navigate to the `build.gradle` file inside the `app` dependency and add the snippet below under the `dependencies` section:

```xml
dependencies {
    //remaining dependencies here
    constraints {
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
            because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
        }
        implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
            because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
        }
    }
    implementation("io.appwrite:sdk-for-android:2.0.0")
}
```

The snippet above adds a dependency constraint to the Kotlin version and Appwrite SDK as a required dependency. We must also re-sync the dependencies to ensure our Android build has no problems.

**Configure OAuth callback**

In order for our application to redirect to the configured screen after authenticating through OAuth 2.0, we must capture the Appwrite OAuth callback URL. To do this, we need to update the `app/main/AndroidManifest.xml` file and add the snippet below under the `<application>` section:

```xml
    <activity android:name="io.appwrite.views.CallbackActivity" android:exported="true">
        <intent-filter android:label="android_web_auth">
            <action android:name="android.intent.action.VIEW"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="appwrite-callback-[PROJECT_ID]"/>
        </intent-filter>
    </activity>
```

We must also replace the `[PROJECT_ID]` string with our Appwrite Project ID.

**Create services**

Next, we need to create service files to separate our application logic from the UI. To do this, we need to create a `utils` package inside the `com.example.android_auth` directory and inside this package, create a `Resource.kt` file and add the snippet below:

```kt
package com.example.android_auth.utils

data class ResourceResponse(
    var name: String? = null,
    var email: String? = null,
    var error: Boolean? = null,
)
```

The snippet above creates a data class to represent the response we get from Appwrite when authenticating using OAuth 2.0.

Lastly, we need to create a Kotlin Class `Client.kt` inside the same `utils` package and add the snippet below:

```kt
package com.example.android_auth.utils

import android.content.Context
import com.example.android_auth.MainActivity
import io.appwrite.Client
import io.appwrite.exceptions.AppwriteException
import io.appwrite.services.Account
import java.lang.Exception


private class ClientInit(context: Context) {
    var clientInstance = Client(context)
        .setEndpoint("https://cloud.appwrite.io/v1")
        .setProject("REPLACE WITH PROJECT ID")
}

class Client {
    suspend fun loginWithGoogle(activity: MainActivity, context: Context): ResourceResponse {
        var account = Account(ClientInit(context).clientInstance)
        try {
            account.createOAuth2Session(activity = activity, provider = "google")
            var data = account.get()
            return ResourceResponse(name = data.name, email = data.email, error = false)
        } catch (e: AppwriteException) {
            return ResourceResponse(error = true)
        }
    }

    suspend fun logOut(context: Context) {
        var account = Account(ClientInit(context).clientInstance)
        try {
            account.deleteSessions()
        } catch (e: AppwriteException) {
            throw Exception("error login out")
        }
    }
}
```

The snippet above does the following:

-   Imports the required dependencies
-   Creates a private class `ClientInit` to initialize Appwrite instance
-   Creates a `Client` class with a `loginWithGoogle` and `logOut` method that uses the Appwrite instance to login with Google using `google` as the provider and log out by deleting active sessions, respectively

**Using the services**

With that done, we can start using the created services to build our application. To do this, first, we need to modify the `Navigation.kt` file as shown below:

```kt
//depencies goes here

@Composable
fun Navigation(activity: MainActivity) {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = Screen.Login.route) {
        composable(route = Screen.Login.route) {
            Login(navController, activity)
        }
        composable(route = "${Screen.Home.route}/{name}/{email}") { it ->
            Home(navController, it.arguments?.getString("name"), it.arguments?.getString("email"))
        }
    }
}
```

The snippet above modifies the route to accept `name` and `email` as part of the arguments and adds them as an argument to the `Home` screen. We will get an error after passing the arguments, but we will fix this shortly.

Secondly, we need to modify the `Login.kt` file inside the `screens` folder as shown below:

```kt
package com.example.android_auth.screens

import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.android_auth.MainActivity
import com.example.android_auth.R
import com.example.android_auth.navigation.Screen
import com.example.android_auth.utils.Client
import kotlinx.coroutines.launch

@Composable
fun Login(navController: NavController, activity: MainActivity) {
    var client = Client()
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier.padding(20.dp)
            .fillMaxWidth()
            .fillMaxHeight()
    ) {
        Column(horizontalAlignment = Alignment.CenterHorizontally) {
            Image(painterResource(id = R.drawable.lock), contentDescription = "lock image")
            Spacer(modifier = Modifier.padding(30.dp))
            Button(
                onClick = {
                    coroutineScope.launch {
                        var data = client.loginWithGoogle(activity, context)
                        if (data.error == true) {
                            Toast.makeText(activity, "Error login in with google", Toast.LENGTH_SHORT).show()
                        } else {
                            navController.navigate("${Screen.Home.route}/${data.name}/${data.email}")
                        }
                    }
                },
                colors = ButtonDefaults.buttonColors(Color(0xFF0165E1)),
                modifier = Modifier
                    .height(45.dp)
                    .fillMaxWidth()
            ) {
                Text(
                    text = "Login with Google",
                    color = Color.White,
                    fontSize = 14.sp,
                    fontWeight = FontWeight.Bold,
                )
            }
        }
    };
}
```

The snippet above does the following:

-   Updates the required dependencies
-   Creates `client`, `context`, and `coroutineScope` properties to initialize Appwrite instance, get application context, and perform an asynchronous operation, respectively
-   Modifies the button’s `onClick` property to use the `loginWithGoogle` method, handles the error appropriately, and navigates to the `Home` screen by passing the returned data as route arguments

Lastly, we need to modify the `Home.kt` file as shown below:

```kt
package com.example.android_auth.screens

import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import com.example.android_auth.navigation.Screen
import com.example.android_auth.utils.Client
import kotlinx.coroutines.launch

@Composable
fun Home(navController: NavController, name: String?, email: String?) {
    var client = Client()
    val context = LocalContext.current
    val coroutineScope = rememberCoroutineScope()

    Column(
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = Modifier
            .padding(20.dp)
            .fillMaxWidth()
            .fillMaxHeight()
    ) {
        Text(
            text = "Welcome $name",
            color = Color(0xFF0165E1),
            fontSize = 20.sp,
            fontWeight = FontWeight.Bold,
        )
        Spacer(modifier = Modifier.padding(10.dp))
        Text(text = "$email")
        Spacer(modifier = Modifier.padding(10.dp))
        Button(
            onClick = {
                coroutineScope.launch {
                    client.logOut(context);
                    navController.navigate(Screen.Login.route) {
                        popUpTo(Screen.Login.route)
                    }
                }
            }, colors = ButtonDefaults.buttonColors(Color.Black),
            modifier = Modifier
                .height(45.dp)
        ) {
            Text(
                text = "Log out",
                color = Color.White,
                fontSize = 14.sp,
                fontWeight = FontWeight.Bold,
            )
        }
    }
}
```

The snippet above does the following:

-   Updates the required dependencies
-   Modifies the function definition to accept `name` and `email` as parameters
-   Creates `client`, `context`, and `coroutineScope` properties to initialize Appwrite instance, get application context, and perform an asynchronous operation, respectively
-   Updates the UI placeholder **name** and **email** with the parameters
-   Modifies the button’s `onClick` property to use the `logOut` method to log users out of the application and redirect them to the **Login** screen

With that done, we can test our application by clicking the **Run** button.

![](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZW5vdWY5YzBzaXAwamhjZXBnODE5dGs2bXBxNGl1a2R4ZXJ1M2FmaiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/IEChXn4VIhI56qKMYJ/giphy.gif)

We can also confirm logged-in users on our Appwrite console.

![logged-in users](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688159858760_Screenshot+2023-06-29+at+00.24.31.png)

**Missing redirect URL error**

When testing our application, we might notice an error screen complaining about a missing URL. It’s an open [issue](https://github.com/appwrite/appwrite/issues/3930) that should be resolved soon.

![missing url](https://paper-attachments.dropboxusercontent.com/s_E7758E847004D32D148542E901EE2B3754F816362E94ECC61E4AF8B0DACEF45B_1688682514793_Screenshot_20230706_232715.png)

## Conclusion

This post detailed a step-by-step guide to implementing Google OAuth 2.0 login in an Android application using [Appwrite](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=hackmamba-blog). Beyond what was discussed above, Appwrite’s Android SDK also has robust functionalities that developers can leverage to build even more robust authorization and authentication mechanisms.

These resources may also be helpful:

-   [Appwrite OAuth support](https://appwrite.io/docs/authentication#oauth?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Appwrite’s Android SDK](https://github.com/appwrite/sdk-for-android?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Appwrite official documentation](https://appwrite.io/docs?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Google Cloud console](https://console.cloud.google.com/)