Firebase Email Password Authentication in Jetpack Compose: A Step-by-Step Guide

Firebase provides a robust and easy-to-implement authentication solution for mobile apps. Combining Firebase Authentication with Jetpack Compose—Google’s modern toolkit for building native UI—you can create seamless and intuitive login experiences. This guide delves into implementing Firebase Email Password Authentication in Jetpack Compose, focusing on best practices and advanced use cases.

Table of Contents

  1. Introduction to Firebase Authentication

  2. Setting Up Firebase in Your Android Project

  3. Configuring Email Password Authentication in Firebase

  4. Jetpack Compose: Building the UI

  5. Implementing Firebase Authentication Logic

  6. Advanced Techniques for Secure Authentication

  7. Conclusion and Best Practices

1. Introduction to Firebase Authentication

Firebase Authentication is a feature of Firebase that enables developers to manage user authentication easily. It supports multiple authentication providers, including email/password, Google, Facebook, and more. Here, we focus on email/password authentication, which is widely used in many applications.

Why Use Firebase Authentication?

  • Ease of Implementation: Pre-built APIs for user authentication.

  • Scalability: Handles millions of users effortlessly.

  • Security: Built-in support for secure authentication and encrypted user data.

  • Integration: Works seamlessly with other Firebase services.

Why Combine Firebase with Jetpack Compose?

Jetpack Compose simplifies UI development by using a declarative approach. Coupled with Firebase Authentication, you can:

  • Build dynamic UIs that respond to authentication state changes.

  • Reduce boilerplate code.

  • Implement modern design patterns like MVVM.

2. Setting Up Firebase in Your Android Project

Before diving into the code, ensure your project is connected to Firebase.

Steps to Set Up Firebase:

  1. Create a Firebase Project:

  2. Add Your Android App:

    • Register your app by entering the package name.

    • Download the google-services.json file and place it in the app/ directory.

  3. Add Firebase SDK to Your Project:

    • Modify the build.gradle files:

      // Root-level build.gradle
      dependencies {
          classpath 'com.google.gms:google-services:4.3.15'
      }
      // Module-level build.gradle
      dependencies {
          implementation 'com.google.firebase:firebase-auth:22.1.0'
      }
      
      apply plugin: 'com.google.gms.google-services'
  4. Sync the Project: Sync your Gradle files to download the dependencies.

3. Configuring Email Password Authentication in Firebase

Enable email/password authentication in the Firebase Console:

  1. Navigate to Authentication > Sign-in Method.

  2. Enable the Email/Password sign-in option.

  3. Save your changes.

4. Jetpack Compose: Building the UI

The UI is an essential part of any authentication flow. Here’s how to build it using Jetpack Compose.

Login Screen UI

Create a composable for the login screen:

@Composable
fun LoginScreen(
    onLogin: (String, String) -> Unit,
    onRegister: () -> Unit
) {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        TextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(8.dp))

        TextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Password") },
            modifier = Modifier.fillMaxWidth(),
            visualTransformation = PasswordVisualTransformation()
        )

        Spacer(modifier = Modifier.height(16.dp))

        Button(onClick = { onLogin(email, password) }) {
            Text("Login")
        }

        Spacer(modifier = Modifier.height(8.dp))

        TextButton(onClick = onRegister) {
            Text("Register")
        }
    }
}

Register Screen UI

Similarly, create a composable for user registration:

@Composable
fun RegisterScreen(
    onRegister: (String, String) -> Unit
) {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        TextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(8.dp))

        TextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Password") },
            modifier = Modifier.fillMaxWidth(),
            visualTransformation = PasswordVisualTransformation()
        )

        Spacer(modifier = Modifier.height(16.dp))

        Button(onClick = { onRegister(email, password) }) {
            Text("Register")
        }
    }
}

5. Implementing Firebase Authentication Logic

Now, integrate Firebase Authentication with your UI.

Login Logic

fun loginUser(email: String, password: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
    FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                onSuccess()
            } else {
                onError(task.exception?.message ?: "Unknown error")
            }
        }
}

Register Logic

fun registerUser(email: String, password: String, onSuccess: () -> Unit, onError: (String) -> Unit) {
    FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password)
        .addOnCompleteListener { task ->
            if (task.isSuccessful) {
                onSuccess()
            } else {
                onError(task.exception?.message ?: "Unknown error")
            }
        }
}

6. Advanced Techniques for Secure Authentication

Using ViewModel for State Management

Leverage Jetpack Compose’s integration with ViewModel for better state management.

class AuthViewModel : ViewModel() {
    private val _authState = MutableStateFlow<AuthState>(AuthState.Idle)
    val authState: StateFlow<AuthState> = _authState

    fun login(email: String, password: String) {
        _authState.value = AuthState.Loading
        loginUser(email, password, {
            _authState.value = AuthState.Success
        }, {
            _authState.value = AuthState.Error(it)
        })
    }
}

sealed class AuthState {
    object Idle : AuthState()
    object Loading : AuthState()
    object Success : AuthState()
    data class Error(val message: String) : AuthState()
}

Encrypting User Data

Always encrypt sensitive user data using libraries like EncryptedSharedPreferences for local storage.

Multi-Factor Authentication

For enhanced security, consider integrating Firebase’s multi-factor authentication capabilities.

7. Conclusion and Best Practices

Firebase Email Password Authentication combined with Jetpack Compose simplifies building secure and modern login systems. Here are a few best practices:

  1. Error Handling: Always handle errors gracefully and provide user-friendly messages.

  2. Security: Use HTTPS, secure Firebase rules, and multi-factor authentication.

  3. UI/UX: Ensure the UI is intuitive and responsive.

By following this guide, you can efficiently implement Firebase Authentication in your Jetpack Compose projects, ensuring a robust and user-friendly experience.