# Firebase Email/Password Authentication: An In-Depth Guide for Android Developers using Kotlin

> This comprehensive guide on Firebase Email Authentication covers everything from understanding Firebase's handling of authentication to building a complete

Source: https://krishparekhdev.com/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin
Published: 2023-02-05
Tags: firebase, authentication, coroutines, android, mobile-development

---

Continuing from our [previous blog](/blog/kotlin-and-firebase-a-hands-on-guide-to-authentication), where we discussed the basics of Firebase and the introduction to Firebase authentication, we now dive deeper into the implementation of Firebase email and password authentication in your Android app using Kotlin. Follow along with the [GitHub project](https://github.com/Krish-Parekh/Firebase-Auth-Blog).

In this comprehensive guide, we'll show you step-by-step how to set up Firebase email and password authentication in your Android app, building upon the foundation laid in our [previous blog](/blog/kotlin-and-firebase-a-hands-on-guide-to-authentication). From creating a Firebase project to integrating the authentication process, we'll provide you with all the information and best practices you need to make your app's login process seamless and secure.

## How firebase handles authentication?

Firebase ensures the identity of a user using various secure methods such as email/password, phone number, or third-party providers such as Google or Facebook. The email authentication process involves the following steps:

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/01.webp)

1. The user provides their credentials (such as email and password) to Firebase.

2. Firebase securely transmits the credentials to its servers for verification.

3. Firebase checks the provided credentials against its database of registered users.

4. If the provided credentials match an existing user, Firebase generates a secure token and sends it back to the app.

5. The app stores the token locally and sends it with each subsequent request to Firebase.

6. Firebase verifies the token on each request to ensure that the user is still authenticated.

7. If the token is valid, Firebase grants the user access to its resources.

## Understanding the firebase authentication token

The secure token generated by Firebase during the authentication process is stored on the client side, typically in the device's local storage or memory Firebase provides [JSON Web Tokens (JWTs)](https://jwt.io/) for authentication. JWTs are a compact and self-contained way to represent claims to be transferred between two parties, such as between a **client and a server**. In the context of Firebase authentication, the JWT contains information about the user and the authentication session, such as the user ID, the issued and expiration timestamps, and a signature that verifies that the token was generated by Firebase. Later in this guide, we'll dive into accessing this valuable token in your app.

## From Theory to Practice

Before diving into Firebase authentication in your Android app, there are a few things you need to have in place. These pre-configurations will ensure a smooth integration of authentication into your app, and make the authentication process quick and hassle-free.

### Required Dependencies

Adding Firebase Authentication and lifecycle-ktx dependencies at your app-level [`build.gradle`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/build.gradle) file.

```kotlin
plugins {
    ...
    id 'com.google.gms.google-services'
}

dependencies {
    ...
    // Firebase Authentication Dependency
    implementation 'com.google.firebase:firebase-auth-ktx:21.1.0'

    // Lifecycle Dependency
    implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"
}
```

In the `buildscript` section of your *project-level* [`build.gradle`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/build.gradle) file, you'll need to add the following dependency.

```kotlin
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.3.15'
    }
}
```

### Utility classes

* The [`Resource.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/util/Resource.kt) class helps you manage the authentication response by keeping track of the **request status, data, and error messages**. It simplifies the display of relevant information to the user during the authentication process. It acts as a **wrapper class** for the network response.

```kotlin
enum class Status {
    SUCCESS,
    ERROR,
    LOADING
}

data class Resource<out T>(
    val status: Status,
    val data: T?,
    val message: String?
) {

    companion object {
        fun <T> success(data: T?): Resource<T> {
            return Resource(Status.SUCCESS, data, null)
        }

        fun <T> error(msg: String): Resource<T> {
            return Resource(Status.ERROR, null, msg)
        }

        fun <T> loading(): Resource<T> {
            return Resource(Status.LOADING, null, null)
        }
    }
}
```

* The [`InputValidation.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/util/InputValidation.kt) class helps validate user input during the authentication process. It checks if the **email and password** entered by the user are in the correct format and not empty. This ensures that the user provides valid information for successful authentication.

```kotlin
class InputValidation {
    companion object {
        private const val EMAIL_ADDRESS_PATTERN = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+\$"

        private const val PASSWORD_PATTERN = "^.*(?=.{4,})(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!&\$%&?#_@ ]).*\$"

        fun checkNullity(input: String): Boolean {
            return input.isNotEmpty();
        }

        fun isUsernameValid(username: String): Pair<Boolean, String> {
            if (username.isEmpty()) {
                return Pair(false, "Username cannot be empty.")
            }
            if (username.length > 100) {
                return Pair(false, "Username cannot be more than 100 characters.")
            }
            if (username[0].isDigit()) {
                return Pair(false, "Username cannot start with a number.")
            }
            if (username.matches("^[a-zA-Z0-9 ]+$".toRegex()).not()) {
                return Pair(false, "Username can only contain alphabets and numbers.")
            }
            return Pair(true, "")
        }

        fun isEmailValid(email: String): Pair<Boolean, String> {
            if (email.isEmpty()) {
                return Pair(false, "Email cannot be empty.")
            }
            if (email[0].isDigit()) {
                return Pair(false, "Email cannot start with a number.")
            }
            if (EMAIL_ADDRESS_PATTERN.toRegex().matches(email).not()) {
                return Pair(false, "Email is not valid.")
            }
            return Pair(true, "")
        }

        fun isPasswordValid(password: String): Pair<Boolean, String> {
            if (password.isEmpty()) {
                return Pair(false, "Password cannot be empty.")
            }
            if (PASSWORD_PATTERN.toRegex().matches(password).not()) {
                return Pair(false, "Password is not valid.")
            }
            return Pair(true, "")
        }
    }
}
```

* The [`TextInputUtils.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/util/TextInputUtils.kt) file provides two simple, yet useful extensions for the `TextInputLayout` and `TextInputEditText` widgets in Android.

    * The **first function**, *addTextWatcher*, add a text watcher to the `TextInputLayout` and clears any error message displayed when text is entered into the `EditText`. This ensures that the user doesn't see any irrelevant error messages while typing.

    * The **second function**, *getValue*, returns the value entered into the `TextInputEditText` as a string. This function returns the string entered into the `TextInputEditText` after being trimmed, ensuring that any leading or trailing spaces are removed from the returned value.

```kotlin
fun TextInputLayout.addTextWatcher() {
    editText?.addTextChangedListener {
        error = null
    }
}

fun TextInputEditText.getValue(): String {
    return text.toString().trim()
}
```

* This code defines a custom [`LoadingDialog.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/util/LoadingDialog.kt) class for a context-based dialog with a loading animation.

```kotlin
class LoadingDialog(context: Context) : Dialog(context) {
    init {
        setContentView(R.layout.loading_dialog)
        window?.setLayout(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.WRAP_CONTENT
        )
        window?.setGravity(Gravity.CENTER)
        setCancelable(false)
    }
}
```

### Building UI

* [`activity_main.xml`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/res/layout/activity_main.xml) you'll find buttons that will take you to screens for email authentication. Further, we will be adding more authentication options.

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".MainActivity">

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnEmailAuth"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:backgroundTint="@color/btn_email_auth"
        android:paddingVertical="8dp"
        android:text="@string/btn_email_authentication"
        android:textAllCaps="false"
        app:icon="@drawable/ic_email"
        app:iconGravity="textStart" />

</androidx.appcompat.widget.LinearLayoutCompat>
```

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/02.webp)

* [`activity_email_login.xml`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/res/layout/activity_email_login.xml) includes fields for entering both an email address and a password, and also includes a TextView labeled "Forgot Password?".

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".ui.email_authentication.EmailLoginActivity">

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/header_login"
        android:textAlignment="center"
              android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etEmailContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_email"
        app:errorEnabled="true"
        app:placeholderText="Brandonelouis@gmail.com"
        app:startIconDrawable="@drawable/ic_email">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etPasswordContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_password"
        app:endIconMode="password_toggle"
        app:errorEnabled="true"
        app:startIconDrawable="@drawable/ic_password">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textPassword" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textview.MaterialTextView
        android:id="@+id/ctaForgotPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/forgot_password_prompt"
        android:textAlignment="textEnd"
        android:textAppearance="@style/TextAppearance.Material3.LabelLarge" />

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnLogin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:text="@string/btn_login"
        android:textAllCaps="false" />

    <com.google.android.material.textview.MaterialTextView
        android:id="@+id/ctaSignup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/signup_prompt"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.Material3.LabelMedium" />
</LinearLayout>
```

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/03.webp)

* [`activity_email_signup.xml`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/res/layout/activity_email_signup.xml) includes fields for entering a username, email address and password.

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".ui.email_authentication.EmailSignupActivity">

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/header_signup"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etUsernameContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_username"
        app:errorEnabled="true"
        app:placeholderText="eg. Brandone Louis"
        app:startIconDrawable="@drawable/ic_person">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etUsername"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etEmailContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_email"
        app:errorEnabled="true"
        app:placeholderText="Brandonelouis@gmail.com"
        app:startIconDrawable="@drawable/ic_email">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etPasswordContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_password"
        app:endIconMode="password_toggle"
        app:errorEnabled="true"
        app:startIconDrawable="@drawable/ic_password">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textPassword" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnSignup"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:text="@string/btn_signup"
        android:textAllCaps="false" />

    <com.google.android.material.textview.MaterialTextView
        android:id="@+id/ctaLogin"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/login_prompt"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.Material3.LabelMedium" />

</LinearLayout>
```

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/04.webp)

* [`activity_email_forgot_password.xml`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/res/layout/activity_email_forgot_password.xml) include a field for entering, an email address.

```xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".ui.email_authentication.EmailForgotPasswordActivity">

    <com.google.android.material.textview.MaterialTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="4dp"
        android:text="@string/header_forgot_password"
        android:textAlignment="center"
        android:textAppearance="@style/TextAppearance.Material3.HeadlineMedium" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/etEmailContainer"
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/hint_email"
        app:errorEnabled="true"
        app:placeholderText="Brandonelouis@gmail.com"
        app:startIconDrawable="@drawable/ic_email">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/etEmail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textEmailAddress" />

    </com.google.android.material.textfield.TextInputLayout>

    <com.google.android.material.button.MaterialButton
        android:id="@+id/btnSendEmail"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:text="@string/btn_send_email"
        android:textAllCaps="false" />

</LinearLayout>
```

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/05.webp)

* [`activity_email_home.xml`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/res/layout/activity_email_home.xml) display the user information and provides the functionality to logout and delete the account.

```xml
<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp"
    tools:context=".ui.email_authentication.EmailHomeActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <com.google.android.material.textview.MaterialTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_id"
            android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />

        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/userId"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
            tools:text="53003205035" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">

        <com.google.android.material.textview.MaterialTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_username"
            android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />

        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/username"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
            tools:text="Krish Parekh" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:orientation="horizontal">

        <com.google.android.material.textview.MaterialTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/label_email"
            android:textAppearance="@style/TextAppearance.Material3.TitleMedium" />

        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/email"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="2"
            android:textAppearance="@style/TextAppearance.Material3.TitleSmall"
            tools:text="krish@gmail.com" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:orientation="horizontal">

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btnLogout"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginHorizontal="8dp"
            android:layout_weight="1"
            android:text="@string/btn_logout"
            android:textAllCaps="false" />

        <com.google.android.material.button.MaterialButton
            android:id="@+id/btnDeleteAccount"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_marginHorizontal="8dp"
            android:layout_weight="1"
            android:text="@string/btn_delete_account"
            android:textAllCaps="false" />
    </LinearLayout>
</LinearLayout>
```

![](/images/blog/firebase-emailpassword-authentication-an-in-depth-guide-for-android-developers-using-kotlin/06.webp)

### Business Logic

The [`EmailAuthViewModel`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/viewmodel/EmailAuthViewModel.kt) is a class that handles the authentication-related business logic for your application using Firebase. It serves as a bridge between the Firebase authentication and the user interface. Currently, it uses ***callback functions*** to communicate the result of each action to the UI. However, this results in verbose code that can get difficult to read and maintain over time. Further, we will ***convert this code to use coroutines*** which will help simplify the code and make it easier to read and understand. This will remove the ***"callback hell"*** and make our code more readable and maintainable in the long run.

The class has data classes and several **live data objects** that represent the status of different authentication operations like **login, signup, password reset, signout, and delete accounts**. These live data objects are observed by the UI and update in real time, keeping the user informed of the **current state of the authentication process**.

| Functions | Description |
| --- | --- |
| `signInWithEmailAndPassword(email, password)` | the function takes an email and password as argument and signs the user. |
| `createUserWithEmailAndPassword(email, password)` | the function creates a new user account. |
| `sendPasswordResetEmail(email)` | the function sends a password reset email to the user. |
| `signOut` | the function simply signs the user out. |
| `delete` | the function deletes the user's account. |

If the user account creation is successful, the function retrieves the current user from the `mAuth` object and creates a `UserProfileChangeRequest` using the `UserProfileChangeRequest.Builder` class. The username parameter is set as the display name in the profile using `setDisplayName(username)`.

Finally, the function calls the `updateProfile` method on the user object to update the user's profile with the display name.

```kotlin
data class LoginUiState(
    val userId: String,
    val username: String,
    val email: String,
)

class EmailAuthViewModel : ViewModel() {

    private val mAuth: FirebaseAuth by lazy { Firebase.auth }

    private val _loginStatus = MutableLiveData<Resource<LoginUiState>>()
    val loginStatus: LiveData<Resource<LoginUiState>> = _loginStatus

    private val _signupStatus = MutableLiveData<Resource<String>>()
    val signupStatus: LiveData<Resource<String>> = _signupStatus

    private val _resetPasswordStatus = MutableLiveData<Resource<String>>()
    val resetPasswordStatus: LiveData<Resource<String>> = _resetPasswordStatus

    private val _deleteAccountStatus = MutableLiveData<Resource<String>>()
    val deleteAccountStatus: LiveData<Resource<String>> = _deleteAccountStatus

    fun login(email: String, password: String) {
        _loginStatus.postValue(Resource.loading())
        mAuth.signInWithEmailAndPassword(email, password)
            .addOnSuccessListener {
                val user = mAuth.currentUser!!
                val loginUiState = LoginUiState(
                    userId = user.uid,
                    username = user.displayName!!,
                    email = user.email!!
                )
                _loginStatus.postValue(Resource.success(loginUiState))
            }
            .addOnFailureListener { exception ->
                _loginStatus.postValue(Resource.error("Login failed : ${exception.message}"))
            }
    }

    fun signup(
        username: String,
        email: String,
        password: String
    ) {
        _signupStatus.postValue(Resource.loading())
        mAuth.createUserWithEmailAndPassword(email, password)
            .addOnSuccessListener {
                val user = mAuth.currentUser!!
                val profileBuilder = UserProfileChangeRequest.Builder()
                val profile = profileBuilder.setDisplayName(username).build()
                user.updateProfile(profile)
                    .addOnSuccessListener {
                        _signupStatus.postValue(Resource.success("Signup success."))
                    }
                    .addOnFailureListener { exception ->
                        _signupStatus.postValue(Resource.error("Signup failed : ${exception.message}"))
                    }
            }
            .addOnFailureListener { exception ->
                _signupStatus.postValue(Resource.error("Signup failed : ${exception.message}"))
            }
    }

    fun resetPassword(email: String) {
        _resetPasswordStatus.postValue(Resource.loading())
        mAuth.sendPasswordResetEmail(email)
            .addOnSuccessListener {
                _resetPasswordStatus.postValue(Resource.success("Resent email sent."))
            }
            .addOnFailureListener { exception ->
                _resetPasswordStatus.postValue(Resource.error("Resent email failed : ${exception.message}"))
            }
    }

    fun signOut() = mAuth.signOut()

    fun deleteAccount() {
        _deleteAccountStatus.postValue(Resource.loading())
        val user = mAuth.currentUser!!
        user.delete()
            .addOnSuccessListener {
                _deleteAccountStatus.postValue(Resource.success("Delete account success."))
            }
            .addOnFailureListener { exception ->
                _deleteAccountStatus.postValue(Resource.error("Delete account failed : ${exception.message}"))
            }
    }
}
```

[`MainActivity.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/MainActivity.kt) class contains `setupUI` method which attaches a click listener to the **btnEmailAuth** button. When the button is clicked, the code creates a new `Intent` object with `Email/buLoginActivity` as the target class. Finally, the code calls `startActivity` to navigate from the `MainActivity` to the `EmailLoginActivity` .

```kotlin
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupUI()
    }

    private fun setupUI() {
        binding.btnEmailAuth.setOnClickListener {
            val emailIntent = Intent(this, EmailLoginActivity::class.java)
            startActivity(emailIntent)
        }
    }
}
```

The [`EmailLoginActivity`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/EmailLoginActivity.kt) is a class that handles the email authentication process for the user. It uses the viewModels delegate from the viewmodel-ktx library to initialize the `EmailAuthViewModel` object, which is responsible for handling the **login process**.

* `setupUI()`: This method is used to set up the user interface. It sets click listeners on various UI elements like the "**Sign Up**" and "**Forgot Password**" buttons and the login button. It also sets text watchers on the **email and password** text fields to verify their validity using the `InputValidation` class.

* `setupObserver()`: This method sets up an observer to monitor the `loginStatus` of the `EmailAuthViewModel`. If the login process is successful, it navigates the user to the `EmailHomeActivity` class and passes some data such as the *user ID, username, and email*. If the login process fails, it displays an **error message using a toast**.

```kotlin
class EmailLoginActivity : AppCompatActivity() {
    private lateinit var binding: ActivityEmailLoginBinding
    private val emailAuthViewModel by viewModels<EmailAuthViewModel>()
    private val loadingDialog by lazy { LoadingDialog(this) }
    companion object {
        const val UID = "uid"
        const val USERNAME = "username"
        const val EMAIL = "email"
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityEmailLoginBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupUI()
        setupObserver()
    }

    private fun setupUI() {
        binding.ctaSignup.setOnClickListener {
            val signupIntent = Intent(this, EmailSignupActivity::class.java)
            startActivity(signupIntent)
        }

        binding.ctaForgotPassword.setOnClickListener {
            val forgetPassIntent = Intent(this, EmailForgotPasswordActivity::class.java)
            startActivity(forgetPassIntent)
        }

        binding.etEmailContainer.addTextWatcher()
        binding.etPasswordContainer.addTextWatcher()

        binding.btnLogin.setOnClickListener {
            val email = binding.etEmail.getValue()
            val password = binding.etPassword.getValue()
            if (verifyDetails(email, password)) {
                emailAuthViewModel.login(email, password)
            }
        }
    }

    private fun setupObserver() {
        emailAuthViewModel.loginStatus.observe(this) { login ->
            when (login.status) {
                LOADING -> {
                    loadingDialog.show()
                }
                SUCCESS -> {
                    loadingDialog.dismiss()
                    val state = login.data!!
                    navigateToHome(state)
                }
                ERROR -> {
                    loadingDialog.dismiss()
                    val error = login.message!!
                    Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun navigateToHome(state: LoginUiState) {
        val homeIntent = Intent(this, EmailHomeActivity::class.java)
        homeIntent.putExtra(UID, state.userId)
        homeIntent.putExtra(USERNAME, state.username)
        homeIntent.putExtra(EMAIL, state.email)
        startActivity(homeIntent)
        finish()
    }

    private fun verifyDetails(email: String, password: String): Boolean {
        val (isEmailValid, emailError) = InputValidation.isEmailValid(email)
        if (isEmailValid.not()) {
            binding.etEmailContainer.error = emailError
            return isEmailValid
        }

        val (isPasswordValid, passwordError) = InputValidation.isPasswordValid(password)
        if (isPasswordValid.not()) {
            binding.etPasswordContainer.error = passwordError
            return isPasswordValid
        }
        return true
    }
}
```

The [`EmailSignupActivity.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/EmailSignupActivity.kt) handles the sign-up process. In the `setupUI` method, user input is verified and the sign-up function of the `EmailAuthViewModel` is called to sign up the user. In the `setupObserver` method, the sign-up status is monitored and the UI is updated accordingly.

```kotlin
class EmailSignupActivity : AppCompatActivity() {
    private lateinit var binding: ActivityEmailSignupBinding
    private val emailAuthViewModel by viewModels<EmailAuthViewModel>()
    private val loadingDialog by lazy { LoadingDialog(this) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityEmailSignupBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupUI()
        setupObserver()
    }

    private fun setupUI() {
        binding.ctaLogin.setOnClickListener {
            finish()
        }

        binding.etUsernameContainer.addTextWatcher()
        binding.etEmailContainer.addTextWatcher()
        binding.etPasswordContainer.addTextWatcher()

        binding.btnSignup.setOnClickListener {
            val username = binding.etUsername.getValue()
            val email = binding.etEmail.getValue()
            val password = binding.etPassword.getValue()
            if (verifyDetails(username, email, password)) {
                emailAuthViewModel.signup(username, email, password)
            }
        }
    }

    private fun setupObserver() {
        emailAuthViewModel.signupStatus.observe(this) { signup ->
            when (signup.status) {
                LOADING -> {
                    loadingDialog.show()
                }
                SUCCESS -> {
                    loadingDialog.dismiss()
                    val state = signup.data!!
                    Toast.makeText(this, state, Toast.LENGTH_SHORT).show()
                    finish()
                }
                ERROR -> {
                    loadingDialog.dismiss()
                    val error = signup.message!!
                    Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun verifyDetails(
        username: String,
        email: String,
        password: String
    ): Boolean {
        val (isUsernameValid, usernameError) = InputValidation.isUsernameValid(username)
        if (isUsernameValid.not()) {
            binding.etUsernameContainer.error = usernameError
            return isUsernameValid
        }

        val (isEmailValid, emailError) = InputValidation.isEmailValid(email)
        if (isEmailValid.not()) {
            binding.etEmailContainer.error = emailError
            return isEmailValid
        }

        val (isPasswordValid, passwordError) = InputValidation.isPasswordValid(password)
        if (isPasswordValid.not()) {
            binding.etPasswordContainer.error = passwordError
            return isPasswordValid
        }
        return true
    }
}
```

The [`EmailForgotPasswordActivity.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/EmailForgotPasswordActivity.kt) file handles the process of forgotten passwords.

```kotlin
class EmailForgotPasswordActivity : AppCompatActivity() {
    private lateinit var binding: ActivityEmailForgotPasswordBinding
    private val emailAuthViewModel by viewModels<EmailAuthViewModel>()
    private val loadingDialog by lazy { LoadingDialog(this) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityEmailForgotPasswordBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupUI()
        setupObserver()
    }

    private fun setupUI() {
        binding.etEmailContainer.addTextWatcher()

        binding.btnSendEmail.setOnClickListener {
            val email = binding.etEmail.getValue()
            if (verifyDetail(email)) {
                emailAuthViewModel.resetPassword(email)
            }
        }
    }

    private fun setupObserver() {
        emailAuthViewModel.resetPasswordStatus.observe(this) { resetPassword ->
            when (resetPassword.status) {
                LOADING -> {
                    loadingDialog.show()
                }
                SUCCESS -> {
                    loadingDialog.dismiss()
                    val state = resetPassword.data!!
                    Toast.makeText(this, state, Toast.LENGTH_SHORT).show()
                    finish()
                }
                ERROR -> {
                    loadingDialog.dismiss()
                    val error = resetPassword.message!!
                    Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    private fun verifyDetail(email: String): Boolean {
        val (isEmailValid, emailError) = InputValidation.isEmailValid(email)
        if (isEmailValid.not()) {
            binding.etEmailContainer.error = emailError
            return isEmailValid
        }
        return true
    }
}
```

The [`EmailHomeActivity.kt`](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/EmailHomeActivity.kt) is the go-to place for a user once they have successfully logged in. It showcases information about the current user and offers options such as ***deleting*** the account and ***logging out***.

```kotlin
class EmailHomeActivity : AppCompatActivity() {
    private lateinit var binding: ActivityEmailHomeBinding
    private val emailAuthViewModel by viewModels<EmailAuthViewModel>()
    private val loadingDialog by lazy { LoadingDialog(this) }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityEmailHomeBinding.inflate(layoutInflater)
        setContentView(binding.root)
        setupUI()
        setupObserver()
    }

    private fun setupUI() {
        val bundle = intent.extras!!
        val uid = bundle.getString(EmailLoginActivity.UID)
        val username = bundle.getString(EmailLoginActivity.USERNAME)
        val email = bundle.getString(EmailLoginActivity.EMAIL)

        binding.userId.text = uid
        binding.username.text = username
        binding.email.text = email

        binding.btnLogout.setOnClickListener {
            emailAuthViewModel.signOut()
            navigateToLogin()
        }

        binding.btnDeleteAccount.setOnClickListener {
            emailAuthViewModel.deleteAccount()
            navigateToLogin()
        }
    }
    private fun setupObserver() {
        emailAuthViewModel.deleteAccountStatus.observe(this){ delete ->
            when(delete.status){
                LOADING -> {
                    loadingDialog.show()
                }
                SUCCESS -> {
                    loadingDialog.dismiss()
                    val state = delete.data!!
                    Toast.makeText(this, state, Toast.LENGTH_SHORT).show()
                    navigateToLogin()
                }
                ERROR -> {
                    loadingDialog.dismiss()
                    val error = delete.message!!
                    Toast.makeText(this, error, Toast.LENGTH_SHORT).show()
                }
            }

        }
    }

    private fun navigateToLogin() {
        finish()
        val loginIntent = Intent(this, EmailLoginActivity::class.java)
        startActivity(loginIntent)
    }
}
```

## Escaping "Callback Hell" with "Coroutines"

### What is CallBack Hell?

* [**Callback Hell**](http://callbackhell.com/) is a term used to describe a situation in which the code for handling asynchronous operations becomes deeply nested and difficult to read and maintain.

* In the signup code, we have multiple nested callbacks to handle the results of asynchronous operations. `createUserWithEmailAndPassword` and `updateProfile` both of these operations are asynchronous and the results are handled using `addOnSuccessListener` and `addOnFailureListener` callback.

* The problem with callback hell is that it makes the code **harder to read, maintain, and debug.**

```kotlin
fun signup(...) {
        mAuth.createUserWithEmailAndPassword(email, password)
            .addOnSuccessListener {
               ...
                user.updateProfile(profile)
                    .addOnSuccessListener {
                        ...
                    }
                    .addOnFailureListener { exception ->
                        ...
                    }
            }
            .addOnFailureListener { exception ->
                ...
            }
    }
```

### What is Coroutine?

* Coroutines are a powerful and lightweight tool for handling asynchronous operations in Kotlin. They allow you to write asynchronous code in a way that is more concise and readable than traditional callback-based approaches.

* It allows us to write asynchronous code in a way that is similar to writing synchronous code. we can use suspend function to sequentially run asynchronous code.

* To learn more about coroutine [link](https://amitshekhar.me/blog/kotlin-coroutines).

### Leveraging Coroutine

[EmailAuthViewModel.kt](https://github.com/Krish-Parekh/Firebase-Auth-Blog/blob/master/app/src/main/java/com/example/firebase_blog/ui/email_authentication/viewmodel/EmailAuthViewModel.kt)

* `viewModelScope.launch(IO)` is used to start a new coroutine within the scope of the ViewModel.

* `viewModelScope` our coroutine is tied to ViewModel, which means the coroutine will be automatically canceled when the ViewModel is destroyed.

* `Dispatcher` is a way to control the execution of coroutines.

    * `IO` is a dispatcher that is optimized for input/output operations, such as reading from or writing to a database or a network.

* `await()` is used to wait for an asynchronous function to complete.

```kotlin
class EmailAuthViewModel : ViewModel() {

    private val mAuth: FirebaseAuth by lazy { Firebase.auth }

    private val _loginStatus = MutableLiveData<Resource<LoginUiState>>()
    val loginStatus: LiveData<Resource<LoginUiState>> = _loginStatus

    private val _signupStatus = MutableLiveData<Resource<String>>()
    val signupStatus: LiveData<Resource<String>> = _signupStatus

    private val _resetPasswordStatus = MutableLiveData<Resource<String>>()
    val resetPasswordStatus: LiveData<Resource<String>> = _resetPasswordStatus

    private val _deleteAccountStatus = MutableLiveData<Resource<String>>()
    val deleteAccountStatus: LiveData<Resource<String>> = _deleteAccountStatus

    fun login(email: String, password: String) {
        viewModelScope.launch(IO) {
            try {
                _loginStatus.postValue(Resource.loading())
                mAuth.signInWithEmailAndPassword(email, password).await()
                val user = mAuth.currentUser!!
                val loginUiState = LoginUiState(
                    userId = user.uid,
                    username = user.displayName!!,
                    email = user.email!!
                )
                _loginStatus.postValue(Resource.success(loginUiState))

            } catch (exception: Exception) {
                _loginStatus.postValue(Resource.error("Login failed : ${exception.message}"))
            }
        }
    }

    fun signup(
        username: String,
        email: String,
        password: String
    ) {
        viewModelScope.launch(IO) {
            try {
                _signupStatus.postValue(Resource.loading())
                mAuth.createUserWithEmailAndPassword(email, password)
                val user = mAuth.currentUser!!
                val profileBuilder = UserProfileChangeRequest.Builder()
                val profile = profileBuilder.setDisplayName(username).build()
                user.updateProfile(profile).await()
                _signupStatus.postValue(Resource.success("Signup success."))
            } catch (exception: Exception) {
                _signupStatus.postValue(Resource.error("Signup failed : ${exception.message}"))
            }
        }
    }

    fun resetPassword(email: String) {
        viewModelScope.launch(IO) {
            try {
                _resetPasswordStatus.postValue(Resource.loading())
                mAuth.sendPasswordResetEmail(email).await()
                _resetPasswordStatus.postValue(Resource.success("Resent email sent."))
            } catch (exception: Exception) {
                _resetPasswordStatus.postValue(Resource.error("Resent email failed : ${exception.message}"))
            }
        }
    }

    fun signOut() = mAuth.signOut()

    fun deleteAccount() {
        viewModelScope.launch(IO) {
            try {
                _deleteAccountStatus.postValue(Resource.loading())
                val user = mAuth.currentUser!!
                user.delete().await()
                _deleteAccountStatus.postValue(Resource.success("Delete account success."))
            } catch (exception: Exception) {
                _deleteAccountStatus.postValue(Resource.error("Delete account failed : ${exception.message}"))
            }
        }
    }
}
```

## Dealing with Exception in Firebase Authentication

Common exceptions you should take into consideration while dealing with Firebase Authentication.

| Exception | Description |
| --- | --- |
| `FirebaseAuthInvalidCredentialsException` | This exception is thrown if the email address or password provided is invalid. For example, if the password is too short or doesn't meet the minimum requirements set by Firebase. |
| `FirebaseAuthUserCollisionException` | This exception is thrown if the email address provided is already in use by another user. This typically occurs when trying to create a new user |
| `FirebaseAuthInvalidUserException` | This exception is thrown if the user corresponding to the email address provided in the request does not exist. This typically occurs when trying to sign in |
| `FirebaseNetworkException` | This exception is thrown if there is a problem with the network connection, such as a timeout or a lost connection. |
| `FirebaseTooManyRequestsException` | This exception is thrown if the client has made too many requests to Firebase within a short period. This is often used to prevent abuse of the Firebase service. |

## Conclusion

In conclusion, we have covered everything you need to know about Firebase email authentication. From how Firebase handles authentication under the hood, to building a complete project using the Firebase API. We also optimized the code by using coroutines, reducing callback hell and addressing common exceptions that should be considered. If you want to follow along with the blog, you can check out my [GitHub project](https://github.com/Krish-Parekh/Firebase-Auth-Blog). Don't forget to follow me on [GitHub](https://github.com/Krish-Parekh/) and [social media](https://twitter.com/GeekCoder) for more updates. In our next blog, we will be discussing phone number authentication. So stay tuned and keep learning!
