Acceso para Android con FirebaseUI

Este codelab es parte del curso Aspectos avanzados de Android en Kotlin. Aprovecharás al máximo este curso si trabajas con los codelabs de forma secuencial, aunque no es obligatorio. Todos los codelabs del curso se indican en la página de destino de los codelabs de Aspectos avanzados de Android en Kotlin.

Introducción

Cuando creas una app para Android, hay muchos beneficios que puedes obtener si admites el acceso de tus usuarios. Si permites que los usuarios creen una identidad dentro de tu app, podrás brindarles más formas de interactuar con ella.

Con las cuentas personalizadas, los usuarios pueden personalizar su experiencia en la app, interactuar con otros usuarios y conservar y transferir sus datos si usan la app en otro dispositivo (como la Web o un teléfono nuevo).

En este codelab, aprenderás los conceptos básicos para admitir el acceso a tu app con la biblioteca de FirebaseUI. Entre muchas otras cosas, la biblioteca de FirebaseUI facilita el trabajo de los desarrolladores que desean crear un flujo de acceso y se encarga de administrar las cuentas de usuario por ti.

Conocimientos que ya deberías tener

  • Aspectos básicos para compilar una app para Android
  • LiveData y ViewModel

Qué aprenderás

  • Cómo agregar Firebase a tu proyecto
  • Cómo admitir el acceso a tu app para Android
  • Cómo observar el estado de autenticación actual de tu app
  • Cómo cerrar la sesión de los usuarios

Actividades

  • Usa Firebase console para integrar Firebase en tu app.
  • Implementa la función de acceso.
  • Agrega personalizaciones en la app para los usuarios que accedieron a sus cuentas.
  • Implementa el cierre de sesión de los usuarios.

Más información sobre LiveData y ViewModel

Para la app de este codelab, necesitas una comprensión básica de LiveData y ViewModel. Lee las descripciones generales de LiveData y ViewModel si deseas obtener una breve descripción general de estos conceptos.

También puedes completar el curso Developing Android Apps with Kotlin para aprender sobre los temas fundamentales de Android que encontrarás como parte de este codelab. Ese curso está disponible como un curso de Udacity y como un curso de codelabs.

En este codelab, compilarás una app que muestra datos curiosos sobre Android. Lo más importante es que la app tendrá un botón de acceso/salida. Cuando el usuario accede a la app, cualquier dato de Android que se muestre incluirá un saludo para el usuario como una forma de agregar un toque de personalización.

Para descargar la app de ejemplo, puedes hacer lo siguiente:

Download Zip

… o clona el repositorio de GitHub desde la línea de comandos con el siguiente comando y cambia a la rama start del repositorio:

$  git clone https://github.com/googlecodelabs/android-kotlin-login

Importante: Como integrarás la app para usar Firebase, la app de inicio requiere cierta configuración para que se compile y ejecute. Lo harás en el siguiente paso del codelab.

Paso 1: Crea un proyecto de Firebase

Antes de poder agregar Firebase a tu app para Android, debes crear un proyecto de Firebase y conectarlo a la app.

  1. En Firebase console, haz clic en Agregar proyecto.
  2. Selecciona o ingresa un Nombre del proyecto. Puedes asignarle a tu proyecto el nombre que quieras, pero intenta elegir uno que sea relevante para la app que estás compilando.
  3. Haz clic en Continuar.
  4. Puedes omitir la configuración de Google Analytics y elegir la opción No en este momento.
  5. Haz clic en Crear proyecto para terminar de configurar el proyecto de Firebase.

Paso 2: Registra tu app con Firebase

Ahora que tienes un proyecto de Firebase, puedes agregarle tu app para Android.

  1. En el centro de la página de descripción general del proyecto en Firebase console, haz clic en el ícono de Android para iniciar el flujo de trabajo de configuración.
  2. Ingresa el ID de aplicación de tu app en el campo Nombre del paquete de Android. Asegúrate de ingresar el ID que usa tu app, ya que no podrás agregar ni modificar este valor después de registrar tu app en el proyecto de Firebase.
  1. A veces, nos referimos al ID de aplicación como un nombre de paquete.
  2. Encuentra este ID de aplicación en el archivo Gradle, generalmente app/build.gradle (ejemplo de ID: com.yourcompany.yourproject) de tu módulo (de nivel de la app).
  3. Ingresa el certificado de firma SHA-1 de depuración. Para generar esta clave, ingresa el siguiente comando en la terminal de la línea de comandos.
keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android
  1. Haz clic en Registrar app.

Paso 3: Agrega el archivo de configuración de Firebase a tu proyecto

Agrega el archivo de configuración de Firebase para Android a tu app:

  1. Haz clic en Descargar google-services.json para obtener el archivo de configuración de Firebase para Android (google-services.json).
  1. Transfiere tu archivo de configuración al directorio del módulo (nivel de app) de tu app.

Paso 4: Configura tu proyecto de Android para habilitar los productos de Firebase

  1. Agrega el complemento de google-services a tus archivos Gradle para habilitar los productos de Firebase en tu app.
  1. Agrega reglas para incluir el complemento de Google Services en el archivo Gradle (build.gradle) de nivel de raíz (nivel de proyecto). Además, revisa que tengas el repositorio Maven de Google.

build.gradle

buildscript {

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
  }

  dependencies {
    // ...

    // Add the following line:
    classpath 'com.google.gms:google-services:4.3.0'  // Google Services plugin
  }
}

allprojects {
  // ...

  repositories {
    // Check that you have the following line (if not, add it):
    google()  // Google's Maven repository
    // ...
  }
}
  1. En el archivo Gradle (generalmente app/build.gradle) de tu módulo (nivel de app), agrega una línea al final del archivo.

app/build.gradle

apply plugin: 'com.android.application'

android {
  // ...
}

// Add the following line to the bottom of the file:
apply plugin: 'com.google.gms.google-services'  // Google Play services Gradle plugin

Paso 4: Agrega la dependencia de Firebase

En este codelab, el motivo principal para integrar Firebase es tener una forma de crear y administrar usuarios. Para ello, debes agregar una biblioteca de Firebase que te permita implementar el acceso.

  1. Agrega la siguiente dependencia en tu archivo build.gradle (Module:app) para que puedas usar el SDK en tu app. El SDK de firebase-auth permite administrar a los usuarios autenticados de tu aplicación.

app/build.gradle:

implementation 'com.firebaseui:firebase-ui-auth:5.0.0'
  1. Sincroniza tu proyecto con los archivos de Gradle para asegurarte de que todas las dependencias estén disponibles para tu app. Si no se te solicita, selecciona File > Sync Project with Gradle Files en Android Studio o desde la barra de herramientas.

Paso 5: Ejecuta la app y revisa el código

  1. Ejecuta la app en un emulador o dispositivo físico para asegurarte de que tu entorno se haya configurado correctamente para comenzar el desarrollo.

Si se realiza correctamente, deberías ver que la pantalla principal muestra un dato curioso sobre Android y un botón de acceso en la esquina superior izquierda. Por el momento, presionar el botón de acceso no realiza ninguna acción.

En un nivel superior, esta es una app de una sola actividad con varios fragmentos. El archivo MainFragment contiene toda la IU que ves en la siguiente pantalla. (Trabajarás con LoginFragment y SettingsFragment en un codelab de seguimiento).

  1. Familiarízate con el código. En particular, ten en cuenta lo siguiente:
  • FirebaseUserLiveData es la clase que implementarás para observar el usuario actual de Firebase asociado a la app. Usarás la instancia FirebaseAuth como punto de entrada para obtener la información de este usuario en un paso posterior.
  • El MainFragment está vinculado al LoginViewModel. LoginViewModel es la clase que implementarás para usar FirebaseUserLiveData y crear una variable authenticationState. Con esta variable authenticationState, MainFragment puede observar el valor para actualizar la IU según corresponda.

En este paso, usarás Firebase console para configurar los métodos de autenticación que deseas que admita tu app. En este codelab, te enfocarás en permitir que los usuarios accedan con la dirección de correo electrónico que proporcionen o con su Cuenta de Google.

  1. Ve a Firebase console. (Nota: Si aún estás en el flujo de trabajo de Add Firebase, haz clic en la X en la esquina superior izquierda para volver a la consola.
  2. Selecciona tu proyecto si aún no estás en él.
  3. Abre el panel de navegación de la izquierda y selecciona Desarrollar > Autenticación.

  1. Selecciona la pestaña Método de acceso en la barra de navegación superior.

  1. Haz clic en la fila Correo electrónico/contraseña.
  2. En la ventana emergente, activa el interruptor Habilitado y haz clic en Guardar.
  3. Del mismo modo, haz clic en la fila Google.
  4. Activa el interruptor Habilitado, ingresa un Correo electrónico de asistencia del proyecto y haz clic en Guardar.

En esta tarea, implementarás la función de acceso para tus usuarios.

  1. Abre MainFragment.kt.
  2. En el diseño de MainFragment, observa el auth_button. Actualmente, no está configurado para controlar ninguna entrada del usuario.
  3. En onViewCreated(),, agrega un onClickListener a auth_button para llamar a launchSignInFlow().

MainFragment.kt

binding.authButton.setOnClickListener { launchSignInFlow() }
  1. Busca el método launchSignInFlow() en MainFragment.kt. Actualmente, contiene un TODO.
  2. Completa la función launchSignInFlow() como se muestra a continuación.

MainFragment.kt

private fun launchSignInFlow() {
   // Give users the option to sign in / register with their email or Google account.
   // If users choose to register with their email,
   // they will need to create a password as well.
   val providers = arrayListOf(
       AuthUI.IdpConfig.EmailBuilder().build(), AuthUI.IdpConfig.GoogleBuilder().build()

       // This is where you can provide more ways for users to register and 
       // sign in.
   )

   // Create and launch sign-in intent.
   // We listen to the response of this activity with the
   // SIGN_IN_REQUEST_CODE 
   startActivityForResult(
       AuthUI.getInstance()
           .createSignInIntentBuilder()
           .setAvailableProviders(providers)
           .build(),
       MainFragment.SIGN_IN_REQUEST_CODE
   )
}

Esto permite que los usuarios se registren y accedan con su dirección de correo electrónico o su Cuenta de Google. Si el usuario elige registrarse con su dirección de correo electrónico, la combinación de correo electrónico y contraseña que cree será única para tu app. Esto significa que podrá acceder a tu app con esa combinación, pero no podrá acceder a ninguna otra app compatible con Firebase con las mismas credenciales.

  1. En MainFragment.kt, puedes escuchar el resultado del proceso de acceso implementando el método onActivityResult(), como se muestra a continuación. Como iniciaste el proceso de acceso con SIGN_IN_REQUEST_CODE, también puedes escuchar el resultado del proceso de acceso filtrando cuándo se devuelve SIGN_IN_REQUEST_CODE a onActivityResult(). Comienza con algunas instrucciones de registro para saber si el usuario accedió correctamente.

MainFragment.kt

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
   super.onActivityResult(requestCode, resultCode, data)
   if (requestCode == SIGN_IN_REQUEST_CODE) {
       val response = IdpResponse.fromResultIntent(data)
       if (resultCode == Activity.RESULT_OK) {
           // User successfully signed in
           Log.i(TAG, "Successfully signed in user ${FirebaseAuth.getInstance().currentUser?.displayName}!")
       } else {
           // Sign in failed. If response is null the user canceled the
           // sign-in flow using the back button. Otherwise check
           // response.getError().getErrorCode() and handle the error.
           Log.i(TAG, "Sign in unsuccessful ${response?.error?.errorCode}")
       }
   }
}

Ahora tu app debería poder registrar usuarios y permitirles acceder.

  1. Ejecuta la app y verifica que, cuando presiones el botón Acceder, aparezca la pantalla de acceso.
  2. Ahora puedes acceder con tu dirección de correo electrónico y una contraseña, o con tu Cuenta de Google.
  3. No habrá cambios en la IU después de que accedas (implementarás la actualización de la IU en el siguiente paso), pero, si todo funciona correctamente, deberías ver el mensaje de registro Successfully signed in user ${your name}! después de completar el flujo de registro.
  4. También puedes ir a Firebase console y navegar a Develop > Authentication > Users para verificar que la app ahora tenga un usuario registrado.
  5. Ten en cuenta que, cuando los usuarios crean una cuenta para tu app, esta se vincula específicamente solo a tu app y no a ninguna otra que use Firebase para la función de acceso.

En esta tarea, implementarás la actualización de la IU según el estado de autenticación. Cuando el usuario accede, puedes personalizar su pantalla principal mostrando su nombre. También actualizarás el botón Acceder para que sea un botón Salir cuando el usuario haya accedido.

  1. Abre la clase FirebaseUserLiveData.kt, que ya se creó para ti. Lo primero que debes hacer es proporcionar una forma para que otras clases de la app sepan cuándo un usuario accedió o salió de su cuenta. Sin embargo, la clase aún no hace nada, ya que no se actualiza el valor de LiveData.
  2. Como usas la biblioteca FirebaseAuth, puedes escuchar los cambios del usuario que accedió con la devolución de llamada FirebaseUser.AuthStateListener que se implementó para ti como parte de la biblioteca de FirebaseUI. Esta devolución de llamada se activa cada vez que un usuario accede a tu app o sale de ella.
  3. Ten en cuenta que FirebaseUserLiveData.kt define la variable authStateListener. Usarás esta variable para almacenar el valor de LiveData. La variable authStateListener se creó para que puedas comenzar y detener correctamente la escucha de los cambios en el estado de autenticación según el estado de tu aplicación. Por ejemplo, si el usuario pone la app en segundo plano, esta debe dejar de escuchar los cambios de estado de autenticación para evitar posibles pérdidas de memoria.
  4. Actualiza authStateListener para que el valor de tu FirebaseUserLiveData corresponda al usuario actual de Firebase.

FirebaseUserLiveData.kt

private val authStateListener = FirebaseAuth.AuthStateListener { firebaseAuth ->
   value = firebaseAuth.currentUser
}
  1. Abre LoginViewModel.kt.
  2. En LoginViewModel.kt, crea una variable authenticationState basada en el objeto FirebaseUserLiveData que acabas de implementar. Al crear esta variable authenticationState, otras clases ahora pueden consultar si el usuario accedió o no a través de LoginViewModel.

LoginViewModel.kt

val authenticationState = FirebaseUserLiveData().map { user ->
   if (user != null) {
       AuthenticationState.AUTHENTICATED
   } else {
       AuthenticationState.UNAUTHENTICATED
   }
}
  1. Abrir MainFragment.kt.
  2. En el observeAuthenticationState() de MainFragment.kt, puedes usar la variable authenticationState que acabas de agregar en LoginViewModel y cambiar la IU según corresponda. Si hay un usuario que accedió, authButton debe mostrar Salir.

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }

                // TODO 2. If the user is logged in, 
                 // you can customize the welcome message they see by
                 // utilizing the getFactWithPersonalization() function provided

           }
           else -> {
               // TODO 3. Lastly, if there is no logged-in user, 
                // auth_button should display Login and
                //  launch the sign in screen when clicked.
           }
       }
   })
}
  1. Si el usuario accedió, también puedes personalizar el mensaje de bienvenida que ve con la función getFactWithPersonalization() que se proporciona en MainFragment.

MainFragment.kt

binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
  1. Por último, si no hay ningún usuario que haya accedido (cuando authenticationState es cualquier valor que no sea LoginViewModel.AuthenticationState.AUTHENTICATED), auth_button debe mostrar Acceder y abrir la pantalla de acceso cuando se haga clic en él. Tampoco debe haber personalización del mensaje que se muestra.

MainFragment.kt

binding.authButton.text = getString(R.string.login_button_text)
binding.authButton.setOnClickListener { launchSignInFlow() }
binding.welcomeText.text = factToDisplay

Con todos los pasos completados, tu método observeAuthenticationState() final debería ser similar al siguiente código:

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
        // TODO 1. Use the authenticationState variable you just added 
         // in LoginViewModel and change the UI accordingly.
       when (authenticationState) {
            // TODO 2.  If the user is logged in, 
             // you can customize the welcome message they see by
             // utilizing the getFactWithPersonalization() function provided
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)
               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   // TODO implement logging out user in next step
               }
           }
           else -> {
                // TODO 3. Lastly, if there is no logged-in user, 
                 // auth_button should display Login and
                 // launch the sign in screen when clicked.
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. Ejecuta tu app. La IU debería actualizarse según si un usuario accedió o no. Si todo funciona correctamente y accediste a tu cuenta, la pantalla principal debería saludarte por tu nombre y mostrar un dato sobre Android. El botón Login ahora también debería mostrar Logout.

En esta tarea, implementarás la función de cierre de sesión.

Dado que la app permite que los usuarios accedan, también debe proporcionarles una forma de salir. A continuación, se muestra un ejemplo de cómo cerrar la sesión de un usuario con una sola línea de código:

AuthUI.getInstance().signOut(requireContext())
  1. Abre MainFragment.kt.
  2. En el observeAuthenticationState() de MainFragment.kt, agrega la lógica de cierre de sesión para que las funciones de auth_button funcionen correctamente cuando haya un usuario que haya accedido. El resultado final del método se ve como el siguiente código.

MainFragment.kt

private fun observeAuthenticationState() {
   val factToDisplay = viewModel.getFactToDisplay(requireContext())

   viewModel.authenticationState.observe(viewLifecycleOwner, Observer { authenticationState ->
       when (authenticationState) {
           LoginViewModel.AuthenticationState.AUTHENTICATED -> {
               binding.welcomeText.text = getFactWithPersonalization(factToDisplay)

               binding.authButton.text = getString(R.string.logout_button_text)
               binding.authButton.setOnClickListener {
                   AuthUI.getInstance().signOut(requireContext())
               }
           }
           else -> {
               binding.welcomeText.text = factToDisplay

               binding.authButton.text = getString(R.string.login_button_text)
               binding.authButton.setOnClickListener {
                   launchSignInFlow()
               }
           }
       }
   })
}
  1. Ejecuta la app.
  2. Presiona el botón Salir y verifica que el usuario haya salido de la cuenta y que el estado del botón haya cambiado a Acceder.

Puedes encontrar la versión final de la app completa aquí: https://github.com/googlecodelabs/android-kotlin-login.

En este codelab, aprendiste lo siguiente:

  • Cómo agregar Firebase a tu proyecto agregando las dependencias necesarias en tu archivo Gradle y configurando el proyecto en Firebase console
  • Cómo implementar el acceso a tu app con la biblioteca de FirebaseUI y especificar cómo quieres permitir que los usuarios accedan Ten en cuenta que cualquier cuenta que cree un usuario en tu app es específica solo para tu app y no se comparte con todas las apps que utilizan Firebase para la función de acceso.
  • Cómo observar el estado de autenticación actual de tu app con LiveData
  • Cómo cerrar la sesión de los usuarios

En este codelab, se abarcaron los conceptos básicos para admitir el acceso en una app para Android.

En este codelab, permitiste que los usuarios se registraran y accedieran con su dirección de correo electrónico. Sin embargo, con la biblioteca de FirebaseUI, también puedes admitir otros métodos de autenticación, como el acceso con un número de teléfono. Para obtener más información sobre las capacidades de la biblioteca de FirebaseUI y cómo utilizar otras funcionalidades que proporciona, consulta los siguientes recursos:

Para obtener más información sobre las prácticas recomendadas en torno al acceso, consulta estos otros recursos:

Codelabs:

Documentación para desarrolladores de Android:

Videos:

Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de los codelabs de Aspectos avanzados de Android en Kotlin.