Overview
NASA Explorer uses Firebase Authentication to manage user accounts. The authentication system supports email/password-based login and registration, ensuring secure access to personalized features like favorites and comments.
User Experience
Registration
New users can create an account by providing an email address and password (minimum 6 characters). The app validates email format and password strength before submission.
Login
Returning users sign in with their registered email and password. The app maintains session state across app launches.
Access Protected Features
Once authenticated, users can save favorite images, add comments, and sync their data across devices.
All user data is securely stored in Firebase Realtime Database and synced across devices.
Authentication Architecture
AuthService
The AuthService class provides a centralized interface for Firebase Authentication operations:
app/src/main/java/com/ccandeladev/nasaexplorer/data/auth/AuthService.kt
class AuthService @Inject constructor ( private val firebaseAuth: FirebaseAuth ) {
/**
* Performs user login with the provided email and password.
* Uses Firebase Authentication to authenticate the user.
*/
suspend fun login (email: String , password: String ): FirebaseUser ? {
return firebaseAuth. signInWithEmailAndPassword (email, password). await ().user
}
/**
* Registers a new user with cancellation control and exception handling.
*/
suspend fun register (email: String , password: String ): FirebaseUser ? {
return suspendCancellableCoroutine { cancellableContinuation ->
firebaseAuth. createUserWithEmailAndPassword (email, password)
. addOnSuccessListener { it: AuthResult ? ->
val user: FirebaseUser ? = it?.user
cancellableContinuation. resume (user)
}. addOnFailureListener { it: Exception ->
cancellableContinuation. resumeWithException (it)
}
}
}
fun userLogout (){
firebaseAuth. signOut ()
}
fun isUserLogged (): Boolean {
return getCurrentUser () != null
}
private fun getCurrentUser () = firebaseAuth.currentUser
}
Login ViewModel
The LoginScreenViewModel handles login logic with validation and state management:
app/src/main/java/com/ccandeladev/nasaexplorer/ui/loginscreen/LoginScreenViewModel.kt
@HiltViewModel
class LoginScreenViewModel @Inject constructor ( private val authService: AuthService ) : ViewModel () {
private var _isLoading = MutableStateFlow < Boolean >( false )
val isLoading: StateFlow < Boolean > = _isLoading
private var _errorMessage = MutableStateFlow < String >( "" )
val errorMessage: StateFlow < String > = _errorMessage
fun login (email: String , password: String , onNavigateToHome: () -> Unit) {
// Validates empty fields
if (email. isBlank () || password. isBlank ()) {
_errorMessage. value = "Los campos no pueden estar vacios"
return
}
// Validates email format
if ( ! isValidEmail (email)) {
_errorMessage. value = "El correo no es válido"
return
}
// Validates password length (minimum 6 characters)
if (password.length < 6 ) {
_errorMessage. value = "La contraseña debe tener al menos 6 caracteres"
return
}
viewModelScope. launch {
_isLoading. value = true
try {
val result = withContext (Dispatchers.IO) {
authService. login (email = email, password = password)
}
if (result != null ) {
onNavigateToHome ()
} else {
_errorMessage. value = "Error de autenticación.Por favor, revisa tus credenciales."
}
} catch (e: Exception ) {
_errorMessage. value = "Correo o contraseña no válidos."
} finally {
_isLoading. value = false
}
}
}
private fun isValidEmail (email: String ): Boolean {
return android.util.Patterns.EMAIL_ADDRESS. matcher (email). matches ()
}
}
Validation Rules
Email Validation
Must be a valid email format
Checked using Android’s EMAIL_ADDRESS pattern matcher
Password Requirements
Minimum 6 characters
Enforced by Firebase Authentication
Registration Flow
SignUp ViewModel
The SignUpViewModel handles new user registration:
app/src/main/java/com/ccandeladev/nasaexplorer/ui/signupscreen/SignUpViewModel.kt
@HiltViewModel
class SignUpViewModel @Inject constructor ( private val authService: AuthService ) : ViewModel () {
private var _isLoading = MutableStateFlow < Boolean >( false )
val isLoading: StateFlow < Boolean > = _isLoading
private var _errorMessage = MutableStateFlow < String >( "" )
val errorMessage: StateFlow < String > = _errorMessage
fun register (email: String , password: String , onNavigateToHome: () -> Unit) {
if (email. isBlank () || password. isBlank ()) {
_errorMessage. value = "Los campos no pueden estar vacios"
return
}
if ( ! isValidEmail (email = email)) {
_errorMessage. value = "El correo no es válido"
return
}
if (password.length < 6 ) {
_errorMessage. value = "La contraseña debe tener al menos 6 caracteres"
return
}
viewModelScope. launch {
_isLoading. value = true
try {
val result = withContext (Dispatchers.IO) {
authService. register (email = email, password = password)
}
if (result != null ) {
onNavigateToHome ()
}
} catch (e: FirebaseAuthUserCollisionException ) {
_errorMessage. value = "El correo ya está en uso."
} catch (e: Exception ) {
_errorMessage. value = "Se produjo un error durante el registro"
} finally {
_isLoading. value = false
}
}
}
}
The registration flow catches FirebaseAuthUserCollisionException to handle cases where the email is already registered.
State Management
Both ViewModels use Kotlin Flows for reactive state management:
isLoading : Shows/hides loading indicators during authentication
errorMessage : Displays validation or authentication errors to users
Coroutines : All Firebase operations run asynchronously on IO dispatcher
Error Handling
Displayed when email or password fields are blank before submission.
Shown when email doesn’t match the standard email pattern.
Triggered when password is less than 6 characters.
Displayed when Firebase rejects the credentials (wrong email/password).
Specific to registration when the email is already registered.
Dependency Injection
The authentication system uses Hilt for dependency injection:
@HiltViewModel
class LoginScreenViewModel @Inject constructor (
private val authService: AuthService
) : ViewModel ()
This enables easy testing and maintains clean separation of concerns.
Session Management
Firebase Authentication automatically maintains user sessions:
Sessions persist across app restarts
Use isUserLogged() to check authentication status
Call userLogout() to sign out users
Check authentication status on app launch to route users to the appropriate screen (login vs. home).