Firebase Auth

TrailLog has been a single-user app so far — every trail belongs to whoever installed the app, and there's no concept of "signing in." The moment TrailLog wants a user's trails to sync to the cloud or be tied to a specific person, it needs to know who is using it. Building your own account system — a signup form, a password database, a reset-email flow, all secured properly — is a genuinely large and risky undertaking. Firebase Authentication gives TrailLog a ready-made, production-grade account system instead: sign-up, login, and password reset, without hosting a single line of your own auth backend.

Enabling Email/Password Sign-In

Firebase Authentication supports several sign-in methods, and each one has to be turned on explicitly in the Firebase Console before it works. For TrailLog's first pass, open the project created in Setup & Analytics, go to Authentication → Sign-in method, and enable the Email/Password provider. With it enabled, the same Firebase project can now create, verify, and manage accounts identified by an email and password — no separate service, no extra dependency beyond firebase-auth added through the BoM from the previous lesson.

Bridging Firebase's Task API Into Coroutines with .await()

Firebase's Android SDK predates widespread Kotlin coroutine adoption, so most of its asynchronous calls return a Task<T> — Google's own callback-based type, not a suspend function. Rather than attaching listeners by hand, the kotlinx-coroutines-play-services library adds a single extension function, .await(), that suspends until a Task completes and returns its result directly — turning any Firebase call into ordinary, sequential coroutine code. This is a genuinely common pattern worth recognizing: whenever an older Google API hands you a Task instead of a suspend fun, .await() is usually the bridge.

Creating a new TrailLog account

suspend fun signUp(email: String, password: String): Result<String> {
    return try {
        val result = Firebase.auth
            .createUserWithEmailAndPassword(email, password)
            .await() // suspends here until Firebase responds
        val uid = result.user?.uid.orEmpty()
        Result.success(uid)
    } catch (e: Exception) {
        Result.failure(e) // e.g. email already in use, weak password
    }
}

Firebase.auth is the Kotlin extension property for the singleton FirebaseAuth instance. createUserWithEmailAndPassword creates the account on Firebase's servers and, on success, signs the user in immediately — there's no separate "now log them in" step required.

Signing In an Existing User

Returning users go through signInWithEmailAndPassword instead, following exactly the same .await() pattern:

suspend fun signIn(email: String, password: String): Result<String> {
    return try {
        val result = Firebase.auth
            .signInWithEmailAndPassword(email, password)
            .await()
        Result.success(result.user?.uid.orEmpty())
    } catch (e: Exception) {
        Result.failure(e) // e.g. wrong password, no account with this email
    }
}

Checking the Current User and Observing Sign-In State

Firebase.auth.currentUser is a nullable property that reflects whether anyone is signed in right now — null means no one is, a non-null FirebaseUser means someone is, and Firebase persists that session automatically between app launches. For a screen that needs to react the instant sign-in state changes (say, redirecting straight to the trail list once sign-up finishes), an AuthStateListener is the tool:

val authListener = FirebaseAuth.AuthStateListener { auth ->
    val user = auth.currentUser
    if (user != null) {
        // signed in — safe to load this user's trails
    } else {
        // signed out — show the login screen
    }
}

Firebase.auth.addAuthStateListener(authListener)
// ...and Firebase.auth.removeAuthStateListener(authListener) when the screen goes away

The listener fires once immediately with the current state when it's registered, and again every time sign-in, sign-up, or sign-out changes that state — which is exactly what makes it useful for driving a "logged in vs. logged out" UI without polling currentUser manually.

Signing Out

Ending a session is a single, synchronous call — no Task, no .await() needed:

Firebase.auth.signOut()

Any registered AuthStateListener fires again right after this, reporting currentUser as null.

Note: These are correct, working Firebase Authentication calls, but this website's own project doesn't run a live Android Studio build or a real Firebase project, so no account is actually created or signed in on the page itself.

Other Sign-In Providers You Could Add Later

Email/password is the simplest provider to reason about for a tutorial, but it's far from the only one Firebase Authentication supports. Google Sign-In lets a user authenticate with their existing Google account in one tap, and phone number authentication verifies identity with an SMS code instead of a password. Both plug into the exact same FirebaseAuth instance and the exact same currentUser/ AuthStateListener model covered above — adding one later is a matter of enabling the provider and calling a different sign-in method, not switching to a different auth system entirely.

Up next: Every signed-in user now has a stable uid. Cloud Firestore uses that exact UID to make sure each user only ever sees their own trails.