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.
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.
uid. Cloud Firestore uses that exact UID to make sure each user only ever sees their own trails.