How to Use Firebase Authentication in Android: A Step-by-Step Guide

How to Use Firebase Authentication in Android: A Step-by-Step Guide


Firebase Authentication is one of the fastest ways to add secure sign-up and login to your Android app. In this beginner-friendly tutorial, you’ll learn how to use Firebase Authentication in Android step by step using Kotlin and Jetpack Compose. We’ll cover email/password login and Google Sign-In with the modern Android Identity stack (Credential Manager + Google ID library), show you how to observe auth state cleanly with coroutines, and share gotchas for phone number and email link flows so you can choose the right method as your app grows.

What is Firebase Authentication and when should you use it?

Android Firebase Authentication flow diagram with phone, auth cloud, secure token, and database arrows.
Authentication flow from Android app to cloud service with secure token and data access.

Firebase Authentication is a managed identity service from Google that lets you add user sign-up and sign-in to your Android apps with minimal backend code. It supports multiple providers (email/password, Google, phone number, email link, and more), handles secure token issuance, and integrates well with other Firebase services.

Use Firebase Auth when you want:

  • A quick, secure login system without building an auth server.
  • Multiple sign-in options (email/password + Google + phone), with the ability to link them into one account.
  • Modern Android UX with Credential Manager and easy integration with Jetpack Compose.
Which sign-in method should you start with?
Provider Setup effort Best for Extra requirements Notes
Email + Password Low Simple MVPs, student projects Enable in Console No billing needed; consider email verification
Google (Credential Manager) Medium Best UX on Android, quick sign-in SHA‑1/256; default_web_client_id Modern flow; clear Credential Manager on sign-out
Phone Number Medium Users without email, quick onboarding SHA‑256; SMS requires billing Use test numbers in dev to avoid charges
Email Link (passwordless) Medium Security-focused flows, fewer passwords Authorized domains; App Links Use Hosting-based flow (not Dynamic Links)

Under the hood, after successful sign-in, the client receives a Firebase ID token (JWT). If your app talks to your own backend, verify this token server-side. Never trust the client-only UID for sensitive operations.

Prerequisites

Android email/password auth mockups: sign up, sign in, and password reset screens without text.
Android sign-up, sign-in, and password reset UI examples for Firebase Authentication.
  • Android Studio (current stable), a basic Kotlin project (Compose recommended), and an emulator or device with Google Play services.
  • A Firebase project (free Spark plan is fine for email/password and Google sign-in; note that sending SMS for phone auth requires Cloud Billing).
  • Basic Kotlin/Android knowledge (we’ll keep code beginner-friendly).

Step 1: Add Firebase to your Android project

1. Create a Firebase project and register your app

  1. Go to Firebase Console > Add project and follow the prompts.
  2. Register your Android app: add your applicationId (e.g., com.example.myapp).
  3. Download google-services.json and place it in app/ of your project.
  4. Add your SHA-1 and SHA-256 fingerprints in Project Settings > Your apps > Android (required for Google Sign-In and recommended for phone auth). You can generate with:
    ./gradlew signingReport

2. Add Gradle dependencies

Use the Firebase Android Bill of Materials (BoM) to manage versions consistently. As of now, the recommended BoM is 34.18.0. If you’re not using the BoM, the current firebase-auth is 24.2.0, but BoM is simpler.

app/build.gradle(.kts)

// Plugins
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
}

android {
// Your usual Android config (compileSdk, defaultConfig, etc.)
}

dependencies {
// Firebase BoM (manages all Firebase versions)
implementation(platform("com.google.firebase:firebase-bom:34.18.0"))
implementation("com.google.firebase:firebase-auth-ktx")

// Coroutines Task.await() for Firebase Tasks
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:<latest>")

// Modern Google Sign-In (Credential Manager + Google ID library)
implementation("androidx.credentials:credentials")
implementation("androidx.credentials:credentials-play-services-auth")
implementation("com.google.android.libraries.identity.googleid:googleid")

// (Compose and other app dependencies)
}

Sync your project. The Google Services Gradle plugin uses google-services.json to configure your project automatically, including a default_web_client_id string we’ll use for Google sign-in.

Step 2: Enable providers in Firebase Console

  1. Go to Authentication > Sign-in method.
  2. Enable Email/Password (and optionally Email link if you plan to use passwordless later).
  3. Enable Google.
  4. (Optional) For Phone, add your SMS region policy and test phone numbers for development. Note: sending SMS requires a Cloud Billing account.
Setup & debugging checklist (Android + Firebase Auth)
  • Add google-services.json to the app/ module and apply the Google Services plugin.
  • Enter both SHA-1 and SHA-256 fingerprints in Firebase project settings.
  • Enable the providers you’ll use: Email/Password, Google, Phone, or Email link.
  • Confirm the default_web_client_id string exists after sync (for Google Sign-In).
  • Whitelist your Authorized domains in Firebase Auth settings (needed for email link).
  • For Phone auth, add test phone numbers for development; real SMS requires billing.
  • Use the Firebase Local Emulator Suite for safe local testing when possible.
  • On sign-out, clear Credential Manager state to avoid stale sessions.

Step 3: Build the Auth ViewModel (Kotlin + coroutines)

We’ll centralize sign-in and sign-out logic inside a ViewModel, expose the current user as a StateFlow for Compose, and add functions for email/password and Google sign-in. We’ll also show how to clear Credential Manager state on sign-out for a clean UX.

Code
class="cd-package">package com.example.myapp.auth

class="cd-package">import android.content.Context
class="cd-package">import androidx.credentials.CredentialManager
class="cd-package">import androidx.credentials.GetCredentialRequest
class="cd-package">import com.google.android.libraries.identity.googleid.GetGoogleIdOption
class="cd-package">import com.google.android.libraries.identity.googleid.GoogleIdCredential
class="cd-package">import com.google.firebase.auth.FirebaseAuth
class="cd-package">import com.google.firebase.auth.FirebaseUser
class="cd-package">import com.google.firebase.auth.GoogleAuthProvider
class="cd-package">import kotlinx.coroutines.flow.MutableStateFlow
class="cd-package">import kotlinx.coroutines.flow.asStateFlow
class="cd-package">import kotlinx.coroutines.tasks.await
class="cd-package">import androidx.lifecycle.ViewModel
class="cd-package">import androidx.credentials.ClearCredentialStateRequest

class AuthViewModel : ViewModel() {

    class="cd-keyword cd-access">private val auth: FirebaseAuth = FirebaseAuth.getInstance()

    class="cd-keyword cd-access">private val _user = MutableStateFlow<FirebaseUser?>(auth.currentUser)
    val user = _user.asStateFlow()

    class="cd-keyword cd-access">private val listener = FirebaseAuth.AuthStateListener { firebaseAuth ->
        _user.value = firebaseAuth.currentUser
    }

    init {
        auth.addAuthStateListener(listener)
    }

    override fun onCleared() {
        auth.removeAuthStateListener(listener)
        super.onCleared()
    }

    suspend fun signUpWithEmail(email: String, password: String): Result<FirebaseUser> {
        return try {
            auth.createUserWithEmailAndPassword(email, password).await()
            Result.success(requireNotNull(auth.currentUser))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    suspend fun signInWithEmail(email: String, password: String): Result<FirebaseUser> {
        return try {
            auth.signInWithEmailAndPassword(email, password).await()
            Result.success(requireNotNull(auth.currentUser))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    suspend fun signInWithGoogle(context: Context): Result<FirebaseUser> {
        return try {
            val credentialManager = CredentialManager.create(context)

            // Get your server client ID from strings (added by google-services.json)
            val serverClientId = context.getString(
                com.example.myapp.R.string.default_web_client_id
            )

            val googleIdOption = GetGoogleIdOption.Builder()
                .setServerClientId(serverClientId)
                // Set to true if you want to restrict to previously authorized accounts
                .setFilterByAuthorizedAccounts(false)
                .build()

            val request = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()

            val result = credentialManager.getCredential(context, request)
            val credential = result.credential

            val googleIdCredential = GoogleIdCredential.createFrom(credential.data)
            val idToken = googleIdCredential.googleIdToken
                ?: error("No Google ID token found")

            val firebaseCred = GoogleAuthProvider.getCredential(idToken, null)
            auth.signInWithCredential(firebaseCred).await()

            Result.success(requireNotNull(auth.currentUser))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    suspend fun linkGoogleToCurrentUser(context: Context): Result<FirebaseUser> {
        val current = auth.currentUser ?: return Result.failure(IllegalStateException("No user"))
        return try {
            val credentialManager = CredentialManager.create(context)

            val serverClientId = context.getString(
                com.example.myapp.R.string.default_web_client_id
            )

            val googleIdOption = GetGoogleIdOption.Builder()
                .setServerClientId(serverClientId)
                .setFilterByAuthorizedAccounts(false)
                .build()

            val request = GetCredentialRequest.Builder()
                .addCredentialOption(googleIdOption)
                .build()

            val result = credentialManager.getCredential(context, request)
            val googleIdCredential = GoogleIdCredential.createFrom(result.credential.data)
            val idToken = googleIdCredential.googleIdToken
                ?: error("No Google ID token found")

            val firebaseCred = GoogleAuthProvider.getCredential(idToken, null)
            current.linkWithCredential(firebaseCred).await()

            Result.success(requireNotNull(auth.currentUser))
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

    suspend fun signOut(context: Context) {
        // Firebase sign out
        auth.signOut()
        // Clear saved Google sign-in state in Credential Manager for a clean next sign-in
        try {
            val cm = CredentialManager.create(context)
            cm.clearCredentialState(ClearCredentialStateRequest())
        } catch (_: Exception) {
            // Ignore; not fatal
        }
    }
}

Step 4: Compose UI – sign up, log in, and Google Sign-In

We’ll build a simple Compose screen with:

  • Email and password fields
  • Buttons for “Create account” and “Sign in”
  • A “Continue with Google” button using Credential Manager
Code
class="cd-package">package com.example.myapp.ui

class="cd-package">import android.widget.Toast
class="cd-package">import androidx.compose.foundation.layout.*
class="cd-package">import androidx.compose.material3.*
class="cd-package">import androidx.compose.runtime.*
class="cd-package">import androidx.compose.ui.Alignment
class="cd-package">import androidx.compose.ui.Modifier
class="cd-package">import androidx.compose.ui.platform.LocalContext
class="cd-package">import androidx.compose.ui.text.input.PasswordVisualTransformation
class="cd-package">import androidx.compose.ui.unit.dp
class="cd-package">import androidx.lifecycle.viewmodel.compose.viewModel
class="cd-package">import com.example.myapp.auth.AuthViewModel
class="cd-package">import kotlinx.coroutines.launch

class="cd-annotation">@Composable
fun AuthGate() {
    val vm: AuthViewModel = viewModel()
    val user by vm.user.collectAsState()

    if (user != null) {
        HomeScreen(onSignOut = { ctx -> 
            // launch sign out
        })
        HomeScreen(onSignOut = { ctx -> })
    } else {
        LoginScreen(vm)
    }
}

class="cd-annotation">@Composable
fun LoginScreen(vm: AuthViewModel) {
    val context = LocalContext.current
    val scope = rememberCoroutineScope()

    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    var loading by remember { mutableStateOf(false) }

    fun showError(e: Throwable) {
        Toast.makeText(context, e.message ?: "Something went wrong", Toast.LENGTH_LONG).show()
    }

    Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
        Column(
            Modifier
                .fillMaxWidth()
                .padding(24.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Welcome", style = MaterialTheme.typography.headlineSmall)
            Spacer(Modifier.height(16.dp))

            OutlinedTextField(
                value = email,
                onValueChange = { email = it.trim() },
                singleLine = true,
                label = { Text("Email") },
                modifier = Modifier.fillMaxWidth()
            )

            Spacer(Modifier.height(8.dp))

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

            Spacer(Modifier.height(16.dp))

            Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
                Button(
                    onClick = {
                        if (email.isBlank() || password.length < 6) {
                            Toast.makeText(context, "Enter a valid email and 6+ char password", Toast.LENGTH_SHORT).show()
                            returnclass="cd-annotation">@Button
                        }
                        scope.launch {
                            loading = true
                            vm.signUpWithEmail(email, password)
                                .onFailure { showError(it) }
                            loading = false
                        }
                    },
                    enabled = !loading
                ) {
                    Text("Create account")
                }

                Button(
                    onClick = {
                        if (email.isBlank() || password.isBlank()) {
                            Toast.makeText(context, "Email and password required", Toast.LENGTH_SHORT).show()
                            returnclass="cd-annotation">@Button
                        }
                        scope.launch {
                            loading = true
                            vm.signInWithEmail(email, password)
                                .onFailure { showError(it) }
                            loading = false
                        }
                    },
                    enabled = !loading
                ) {
                    Text("Sign in")
                }
            }

            Spacer(Modifier.height(24.dp))

            Divider()

            Spacer(Modifier.height(12.dp))

            Button(
                onClick = {
                    scope.launch {
                        loading = true
                        vm.signInWithGoogle(context)
                            .onFailure { showError(it) }
                        loading = false
                    }
                },
                enabled = !loading
            ) {
                Text("Continue with Google")
            }
        }

        if (loading) {
            CircularProgressIndicator(Modifier.align(Alignment.Center))
        }
    }
}

class="cd-annotation">@Composable
fun HomeScreen(onSignOut: (android.content.Context) -> Unit = {}) {
    val context = LocalContext.current
    Column(
        Modifier
            .fillMaxSize()
            .padding(24.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text("You are signed in!", style = MaterialTheme.typography.headlineSmall)
        Spacer(Modifier.height(16.dp))
        Button(onClick = { onSignOut(context) }) {
            Text("Sign out")
        }
    }
}

Wire the onSignOut to vm.signOut(context) in your actual screen or navigation host. In a larger app, observe vm.user to gate navigation (e.g., unauthenticated users cannot reach protected routes).

Step 5: Android Studio Firebase login with email and password

We already implemented the core methods with coroutines:

  • createUserWithEmailAndPassword(email, password) for sign-up
  • signInWithEmailAndPassword(email, password) for login
  • signOut() to sign out

Common errors to handle gracefully:

  • Invalid email: prompt the user to check formatting.
  • Weak password: ensure at least 6 characters (or stronger if you enforce it).
  • User collision (email already in use): suggest using “Sign in” instead of “Create account”.

Tip: If you also enable “Email link (passwordless)”, don’t expose users to email enumeration. Newer Firebase projects have email enumeration protection enabled by default, which can change what fetchSignInMethodsForEmail returns. Design your UX to handle unknown states gracefully.

Step 6: Kotlin Firebase Authentication for Android – Google Sign-In with Credential Manager

The recommended way to add Google Sign-In today is with Android’s Credential Manager and the Google ID library. Don’t use the old GoogleSignInClient. Our ViewModel code shows the full flow:

  1. Build a GetGoogleIdOption with your default_web_client_id (server client ID from google-services.json).
  2. Launch CredentialManager.getCredential(...) to present the native account picker.
  3. Extract the ID token via GoogleIdCredential.createFrom(...).
  4. Exchange with Firebase using GoogleAuthProvider.getCredential(idToken, null).
  5. On sign-out, call CredentialManager.clearCredentialState to reset saved state.

This works back to API 19 and pairs nicely with Compose.

Google Sign-In with Credential Manager: end-to-end flow
1. User taps “Continue with Google”
2. Credential Manager shows account picker (Google ID)
3. App receives Google ID token (JWT)
4. Exchange token with Firebase using GoogleAuthProvider
5. FirebaseAuth signs in; UI observes auth state (StateFlow) and navigates

Optional: Link accounts into a single user

If a user first creates an account with email/password and later taps Google Sign-In, link it so they keep one profile and UID:

// After sign-in with email/password
vm.linkGoogleToCurrentUser(context)
.onSuccess { /* linked */ }
.onFailure { /* show error (e.g., credential already in use) */ }

Use this pattern for any provider, e.g., link phone or email link later.

Phone number and email-link basics (what to know as a beginner)

Phone number sign-in

  • Firebase phone auth uses Play Integrity for app verification on Android. Add your SHA-256 fingerprint in Firebase project settings.
  • If Play services aren’t available, reCAPTCHA is used as a fallback.
  • From September 2024, sending SMS codes requires a Cloud Billing account even on the Spark plan. Use test numbers in Console for development to avoid charges.

Official guide: Authenticate with Firebase on Android using a Phone Number (link below).

Email link (passwordless)

  • Use the modern Hosting-based email link flow (SDK v23.2.0+ / BoM v33.9.0+). Don’t use Dynamic Links for this use case; it was migrated.
  • Configure Authorized Domains in Firebase Auth settings and set up Android App Links to complete sign-in in-app.

Project structure tips for beginners

  • Keep auth code in a dedicated module or package (auth/), with a ViewModel exposing StateFlow<FirebaseUser?>.
  • Gate Compose navigation based on user != null; redirect unauthenticated users to your login screen.
  • Use the Firebase Local Emulator Suite for safe local testing (no accidental SMS or email costs).

Security checklist

  • Verifying users on your server? Always verify the Firebase ID token using Google’s libraries on the backend. Do not trust a raw UID alone.
  • Use Firebase Security Rules for Firestore/Storage and scope data by request.auth.uid.
  • Consider multi-factor auth (SMS or TOTP) by upgrading to Firebase Authentication with Identity Platform if your app needs stronger security.

Java/XML note for classic views

This tutorial focuses on Kotlin + Compose because that’s the modern Android path. If you’re a Java beginner using XML layouts, Firebase Auth works the same conceptually: initialize FirebaseAuth, call createUserWithEmailAndPassword, signInWithEmailAndPassword, and use Credential Manager for Google Sign-In. The main difference is wiring click listeners to start the credential flow and observing auth changes (e.g., via FirebaseAuth.AuthStateListener) to update your activities/fragments.

Output / Result

By the end of this tutorial, your Android app can:

  • Create a new account with email and password.
  • Sign in with email/password or “Continue with Google.”
  • React to login state in real time (show Home when signed in, Login when signed out).
  • Sign out and clear Credential Manager state so the next sign-in is clean.
  • Optionally link Google to an existing email/password user to keep one account.

Common mistakes and how to fix them

  • Forgetting SHA-1/SHA-256: Google Sign-In and phone auth will fail. Add fingerprints in Firebase settings and re-download google-services.json if needed.
  • Using legacy GoogleSignInClient: Switch to Credential Manager + Google ID library for a supported flow.
  • Assuming phone auth works without billing: You can test with fictional numbers, but real SMS requires a billing account.
  • Not clearing Credential Manager state on sign-out: Users may see stale sessions. Call clearCredentialState.
  • Relying on fetchSignInMethodsForEmail for identifier-first UX: With email enumeration protection, design the flow to avoid leaking whether an email exists.

FAQ: People Also Ask

What is Firebase Authentication in Android and why use it?

It’s a managed identity service that lets your Android app sign users in with providers like email/password, Google, phone number, or email link. It saves months of backend work, integrates with other Firebase products, and follows modern security and Android UX patterns.

How do I set up Firebase Authentication in Android Studio?

Add your app to a Firebase project, place google-services.json in app/, enable providers in Firebase Console, add the Google Services plugin, and include firebase-auth-ktx using the Firebase BoM. Then write sign-in code (email/password, Google with Credential Manager) and observe auth state to update your UI.

How can I implement email and password login with Firebase in Android?

Use FirebaseAuth:
createUserWithEmailAndPassword for sign-up, signInWithEmailAndPassword for login, and signOut to sign out. With coroutines, call await() on the returned Tasks and handle exceptions to show friendly error messages.

How do I add Google Sign-In using Firebase in an Android app?

Use Credential Manager and the Google ID library. Build GetGoogleIdOption with your default_web_client_id, get a Google ID token via CredentialManager.getCredential, exchange it with Firebase using GoogleAuthProvider.getCredential, and update your UI based on auth state. Clear Credential Manager state on sign-out for best UX.

Is Firebase Authentication free for Android apps and what are the limits?

Email/password and Google sign-in are free on the Spark plan. Phone auth requires a billing account to send real SMS, though you can use test numbers for development. Review the Firebase pricing page for quotas and regional costs before launching.

Troubleshooting quick tips

  • Google sign-in fails with “12501” or similar: Ensure SHA-1/256 are added and the correct default_web_client_id is used.
  • “This operation is not allowed” on email/password: Check the provider is enabled in Firebase Console.
  • Phone code not arriving: Use test numbers in Console while developing; verify SMS region policy and billing for real numbers.
  • “Blocked by CORS” on backend verify: Verify Firebase ID tokens using official admin SDKs on the server instead of calling Google endpoints from the client.

Summary: How to use Firebase Authentication in Android

You set up Firebase with the Android BoM, enabled Auth providers, built a Kotlin ViewModel with coroutines to handle login flows, implemented email/password and Google Sign-In using Credential Manager, observed auth state with StateFlow, and cleaned up on sign-out. From here, you can expand with phone auth, email link sign-in, account linking, and multi-factor authentication as your app grows.

Sources / Further reading

Keywords used naturally: How to use Firebase Authentication in Android, Firebase Authentication Android tutorial, Android Studio Firebase login, Firebase email password authentication Android, Kotlin Firebase Authentication for Android, Java Firebase Authentication for Android, how to use Firebase Authentication in Android step by step, add email and password login with Firebase in Android Studio, beginner guide to Firebase Authentication in Android Kotlin, create account and sign in with Firebase Auth on Android, set up Google sign in with Firebase in Android app.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted