Codelab web de AngularFire

1. Descripción general

En este codelab, aprenderás a usar AngularFire para crear aplicaciones web implementando un cliente de chat con productos y servicios de Firebase.

angularfire-2.png

Qué aprenderás

  • Compila una app web con Angular y Firebase.
  • Sincronizar datos con Cloud Firestore y Cloud Storage para Firebase
  • Autentica a tus usuarios con Firebase Authentication.
  • Implementar su aplicación web en Firebase Hosting
  • Enviar notificaciones con Firebase Cloud Messaging
  • Recopila los datos de rendimiento de tu app web.

Requisitos

  • El editor de texto o IDE que prefieras, como WebStorm, Atom, Sublime o VS Code
  • El administrador de paquetes npm, que suele incluir Node.js
  • Una terminal/consola
  • Un navegador de tu elección, como Chrome
  • El código de muestra del codelab (consulta el siguiente paso del codelab para ver cómo obtener el código).

2. Obtén el código de muestra

Clona el repositorio de GitHub del codelab desde la línea de comandos:

git clone https://github.com/firebase/codelab-friendlychat-web

Como alternativa, si no tienes instalado Git, puedes descargar el repositorio como un archivo ZIP.

Importa la app de partida

Con el IDE, abre o importa el directorio HOWMANY angularfire-start del repositorio clonado. El directorio targetSdkVersion angularfire-start contiene el código de inicio para el codelab, que será una app web de chat completamente funcional.

3. Crea y configura un proyecto de Firebase

Crea un proyecto de Firebase

  1. Accede a Firebase.
  2. En Firebase console, haz clic en Agregar proyecto y, luego, asigna el nombre FriendlyChat a tu proyecto de Firebase. Recuerda el ID de tu proyecto de Firebase.
  3. Desmarca la opción Habilitar Google Analytics para este proyecto.
  4. Haz clic en Crear proyecto.

La aplicación que compilarás usa los productos de Firebase que están disponibles para las apps web:

  • Firebase Authentication para permitir que los usuarios accedan a tu app con facilidad.
  • Cloud Firestore, a fin de guardar datos estructurados en la nube y recibir notificaciones al instante cuando se modifiquen los datos
  • Cloud Storage para Firebase, a fin de guardar archivos en la nube
  • Firebase Hosting, a fin de alojar y entregar sus recursos
  • Firebase Cloud Messaging, a fin de enviar notificaciones push y mostrar notificaciones emergentes del navegador
  • Firebase Performance Monitoring para recopilar datos de rendimiento de los usuarios sobre tu app.

Algunos de estos productos necesitan una configuración especial o se deben habilitar mediante Firebase console.

Agrega una app web de Firebase al proyecto

  1. Haz clic en el ícono web 58d6543a156e56f9.png para crear una nueva aplicación web de Firebase.
  2. Registra la app con el sobrenombre friendly Chat y, luego, marca la casilla junto a Configurar Firebase Hosting para esta app. Haz clic en Registrar app.
  3. En el siguiente paso, verás un objeto de configuración. Copia solo el objeto JS (no el HTML que lo rodea) en firebase-config.js.

Captura de pantalla de Registrar app web

Habilitar el Acceso con Google para Firebase Authentication

Para permitir que los usuarios accedan a la app web con sus Cuentas de Google, debes usar el método de acceso de Google.

Deberás habilitar el Acceso con Google:

  1. En Firebase console, busca la sección Build en el panel izquierdo.
  2. Haz clic en Authentication y, luego, en la pestaña Sign-in method (o haz clic aquí para ir directamente allí).
  3. Habilita el proveedor de acceso de Google y, luego, haz clic en Guardar.
  4. Establece el nombre público de tu app en Friendly Chat y elige un Correo electrónico de asistencia del proyecto en el menú desplegable.
  5. Configura tu pantalla de consentimiento de OAuth en la consola de Google Cloud y agrega un logotipo:

d89fb3873b5d36ae.png

Habilita Cloud Firestore

La aplicación web usa Cloud Firestore para guardar mensajes de chat y recibir mensajes nuevos.

Deberás habilitar Cloud Firestore:

  1. En la sección Compilación de Firebase console, haz clic en Base de datos de Firestore.
  2. Haz clic en Crear base de datos en el panel de Cloud Firestore.

729991a081e7cd5.png

  1. Selecciona la opción Comenzar en modo de prueba y, luego, haz clic en Siguiente después de leer la renuncia de responsabilidad sobre las reglas de seguridad.

El modo de prueba garantiza que puedas escribir con libertad en la base de datos durante el desarrollo. Más adelante en este codelab, mejorarás la seguridad de nuestra base de datos.

77e4986cbeaf9dee.png

  1. Configura la ubicación en la que se almacenan tus datos de Cloud Firestore. Puedes dejar esta opción con la configuración predeterminada o elegir una región cercana. Haz clic en Listo para aprovisionar Firestore.

9f2bb0d4e7ca49c7.png

Habilita Cloud Storage

La aplicación web usa Cloud Storage para Firebase con el objetivo de almacenar, subir y compartir fotos.

Deberás habilitar Cloud Storage:

  1. En la sección Compilación de Firebase console, haz clic en Almacenamiento.
  2. Si el botón Comenzar no aparece, significa que Cloud Storage ya está habilitado y no es necesario que sigas los pasos que se indican a continuación.
  3. Haz clic en Get Started.
  4. Lee la renuncia de responsabilidad sobre las reglas de seguridad de tu proyecto de Firebase y, luego, haz clic en Siguiente.

Con las reglas de seguridad predeterminadas, cualquier usuario autenticado puede escribir lo que sea en Cloud Storage. Aumentarás la seguridad de nuestro almacenamiento más adelante en este codelab.

62f1afdcd1260127.png

  1. La ubicación de Cloud Storage está preseleccionada con la misma región que elegiste para tu base de datos de Cloud Firestore. Haz clic en Listo para completar la configuración.

1d7f49ebaddb32fc.png

4. Instala la interfaz de línea de comandos de Firebase

La interfaz de línea de comandos (CLI) de Firebase te permite usar Firebase Hosting para entregar tu app web de forma local, así como implementar la app web en tu proyecto de Firebase.

  1. Ejecuta el siguiente comando npm para instalar la CLI:
npm -g install firebase-tools
  1. Ejecuta el siguiente comando para verificar que la CLI se haya instalado de forma correcta:
firebase --version

Asegúrate de que la versión de Firebase CLI sea 4.1.0 o posterior.

  1. Ejecuta el siguiente comando para autorizar Firebase CLI:
firebase login

Configuraste la plantilla de app web para extraer la configuración de tu app para Firebase Hosting desde el directorio local de la app (el repositorio que clonaste antes en el codelab). Sin embargo, para extraer la configuración, debes asociar tu app con tu proyecto de Firebase.

  1. Asegúrate de que la línea de comandos acceda al directorio angularfire-start local de tu app.
  2. Ejecuta el siguiente comando para asociar la app con el proyecto de Firebase:
firebase use --add
  1. Cuando se te solicite, selecciona el ID del proyecto y asígnale un alias a tu proyecto de Firebase.

Un alias es útil si tienes varios entornos (producción, etapa de pruebas, etcétera). Sin embargo, para este codelab, usaremos el alias de default.

  1. Sigue las instrucciones restantes en la línea de comandos.

5. Instala AngularFire

Antes de ejecutar el proyecto, asegúrate de tener configurados la CLI y AngularFire de Angular.

  1. En una consola, ejecuta el siguiente comando:
npm install -g @angular/cli
  1. Luego, en una consola del directorio angularfire-start, ejecuta el siguiente comando de la CLI de Angular:
ng add @angular/fire

De este modo, se instalarán todas las dependencias necesarias para tu proyecto.

  1. Cuando se te solicite, selecciona las funciones que se configuraron en Firebase console (ng deploy -- hosting, Authentication, Firestore, Cloud Functions (callable), Cloud Messaging y Cloud Storage) y sigue las indicaciones que aparecen en la consola.

6. Ejecuta la app de partida de forma local

Ahora que importaste y configuraste tu proyecto, tienes todo listo para ejecutar la app web por primera vez.

  1. En una consola desde el directorio angularfire-start, ejecuta el siguiente comando de Firebase CLI:
firebase emulators:start
  1. Tu línea de comandos debe mostrar la siguiente respuesta:
✔  hosting: Local server: http://localhost:5000

Usas el emulador de Firebase Hosting para entregar la app de forma local. Ahora, la aplicación web debería estar disponible en http://localhost:5000. Se entregan todos los archivos ubicados en el subdirectorio src.

  1. En el navegador, abre tu aplicación en http://localhost:5000.

Deberías ver la IU de tu app de FriendlyChat, que aún no está funcionando:

angularfire-2.png

La app no puede realizar ninguna acción en este momento, pero con tu ayuda, lo hará pronto. Hasta ahora, solo diseñaste la IU por ti.

Vamos a crear un chat en tiempo real.

7. Importa y configura Firebase

Configura Firebase

Deberás configurar el SDK de Firebase para indicarle qué proyecto de Firebase estás usando.

  1. Ve a la configuración de tu proyecto en Firebase console.
  2. En la tarjeta "Tus apps", selecciona el sobrenombre de la app para la que necesitas un objeto de configuración.
  3. Selecciona “Configuración” en el panel de fragmentos del SDK de Firebase.

Verás que se generó un archivo de entorno /angularfire-start/src/environments/environment.ts para ti.

  1. Copia el fragmento del objeto de configuración y, luego, agrégalo a angularfire-start/src/firebase-config.js.

environment.ts

export const environment = {
  firebase: {
    apiKey: "API_KEY",
    authDomain: "PROJECT_ID.firebaseapp.com",
    databaseURL: "https://PROJECT_ID.firebaseio.com",
    projectId: "PROJECT_ID",
    storageBucket: "PROJECT_ID.appspot.com",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID",
    measurementId: "G-MEASUREMENT_ID",
  },
};

Cómo importar AngularFire

Verás que las funciones que seleccionaste en la consola se enrutaron automáticamente en el archivo /angularfire-start/src/app/app.module.ts. Esto permite que tu app use las características y funcionalidades de Firebase. Sin embargo, para desarrollar en un entorno local, debes conectarlos para usar Emulator Suite.

  1. En /angularfire-start/src/app/app.module.ts, busca la sección imports y modifica las funciones proporcionadas para conectarte a Emulator Suite en entornos que no sean de producción.
// ...

import { provideAuth,getAuth, connectAuthEmulator } from '@angular/fire/auth';
import { provideFirestore,getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore';
import { provideFunctions,getFunctions, connectFunctionsEmulator } from '@angular/fire/functions';
import { provideMessaging,getMessaging } from '@angular/fire/messaging';
import { provideStorage,getStorage, connectStorageEmulator } from '@angular/fire/storage';

// ...

provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
    const auth = getAuth();
    if (location.hostname === 'localhost') {
        connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
    }
    return auth;
}),
provideFirestore(() => {
    const firestore = getFirestore();
    if (location.hostname === 'localhost') {
        connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
    }
    return firestore;
}),
provideFunctions(() => {
    const functions = getFunctions();
    if (location.hostname === 'localhost') {
        connectFunctionsEmulator(functions, '127.0.0.1', 5001);
    }
    return functions;
}),
provideStorage(() => {
    const storage = getStorage();
    if (location.hostname === 'localhost') {
        connectStorageEmulator(storage, '127.0.0.1', 5001);
    }
    return storage;
}),
provideMessaging(() => {
    return getMessaging();
}),

// ...

app.module.ts

Durante este codelab, usarás Firebase Authentication, Cloud Firestore, Cloud Storage, Cloud Messaging y Performance Monitoring, por lo que importarás todas sus bibliotecas. En tus apps futuras, asegúrate de importar solo las partes de Firebase que necesites para acortar el tiempo de carga de tu app.

8. Configura el acceso de los usuarios

AngularFire debería estar listo para usarse, ya que se importó y se inicializó en app.module.ts. Ahora, implementarás el acceso de usuarios con Firebase Authentication.

Autentica a tus usuarios mediante el Acceso con Google

En la app, cuando un usuario hace clic en el botón Acceder con Google, se activa la función login. (Ya lo configuraste por ti) En este codelab, debes autorizar a Firebase para que use Google como el proveedor de identidad. Usarás una ventana emergente, pero Firebase dispone de varios otros métodos.

  1. En el directorio angularfire-start, en el subdirectorio /src/app/services/, abre chat.service.ts.
  2. Busca la función login.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Signs-in Friendly Chat.
login() {
    signInWithPopup(this.auth, this.provider).then((result) => {
        const credential = GoogleAuthProvider.credentialFromResult(result);
        this.router.navigate(['/', 'chat']);
        return credential;
    })
}

La función logout se activa cuando el usuario hace clic en el botón Salir.

  1. Regresa al archivo src/app/services/chat.service.ts.
  2. Busca la función logout.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Logout of Friendly Chat.
logout() {
    signOut(this.auth).then(() => {
        this.router.navigate(['/', 'login'])
        console.log('signed out');
    }).catch((error) => {
        console.log('sign out error: ' + error);
    })
}

Haz un seguimiento del estado de autenticación

Para actualizar nuestra IU según corresponda, necesitas una forma de verificar si el usuario accedió o salió. Con Firebase Authentication, puedes recuperar el estado observable del usuario que se activará cada vez que cambie el estado de autenticación.

  1. Regresa al archivo src/app/services/chat.service.ts.
  2. Busca la asignación de variables user$.
  3. Reemplaza toda la tarea por el siguiente código.

chat.service.ts

// Observable user
user$ = user(this.auth);

El código anterior llama a la función user de AngularFire que muestra un usuario observable. Se activará cada vez que cambie el estado de autenticación (cuando el usuario acceda o salga). En este punto, actualizarás la IU para redireccionar, mostrar al usuario en la navegación del encabezado, etc. Ya se implementaron todas estas partes de la IU.

Prueba acceder a la app

  1. Si tu app aún se está publicando, actualízala en el navegador. De lo contrario, ejecuta firebase emulators:start en la línea de comandos para comenzar a entregar la app desde http://localhost:5000 y, luego, ábrela en tu navegador.
  2. Usa el botón de acceso y tu Cuenta de Google para acceder a la app. Si ves un mensaje de error que indica auth/operation-not-allowed, asegúrate de que habilitaste el Acceso con Google como proveedor de autenticación en Firebase console.
  3. Después de acceder, deberían mostrarse tu foto de perfil y nombre de usuario: angularfire-3.png

9. Escriba mensajes en Cloud Firestore

En esta sección, escribirás algunos datos en Cloud Firestore para poder propagar la IU de la app. Esto se puede hacer manualmente con Firebase console, pero lo harás en la app para demostrar una escritura básica de Cloud Firestore.

Modelo de datos

Los datos de Cloud Firestore se dividen en colecciones, documentos, campos y subcolecciones. Almacenarás cada mensaje del chat como un documento en una colección de nivel superior llamada messages.

688b77bc5fb662b57.png

Agrega mensajes a Cloud Firestore

Para almacenar los mensajes de chat que escriben los usuarios, debes usar Cloud Firestore.

En esta sección, agregarás la funcionalidad para que los usuarios escriban mensajes nuevos en tu base de datos. Los usuarios que hagan clic en el botón ENVIAR activarán el fragmento de código que aparece abajo. Este agrega un objeto de mensaje con el contenido de los campos de mensaje a tu instancia de Cloud Firestore en la colección messages. El método add() agrega a la colección un documento nuevo con un ID generado automáticamente.

  1. Regresa al archivo src/app/services/chat.service.ts.
  2. Busca la función addMessage.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Adds a text or image message to Cloud Firestore.
addMessage = async(textMessage: string | null, imageUrl: string | null): Promise<void | DocumentReference<DocumentData>> => {
    let data: any;
    try {
      this.user$.subscribe(async (user) => 
      { 
        if(textMessage && textMessage.length > 0) {
          data =  await addDoc(collection(this.firestore, 'messages'), {
            name: user?.displayName,
            text: textMessage,
            profilePicUrl: user?.photoURL,
            timestamp: serverTimestamp(),
            uid: user?.uid
          })}
          else if (imageUrl && imageUrl.length > 0) {
            data =  await addDoc(collection(this.firestore, 'messages'), {
              name: user?.displayName,
              imageUrl: imageUrl,
              profilePicUrl: user?.photoURL,
              timestamp: serverTimestamp(),
              uid: user?.uid
            });
          }
          return data;
        }
      );
    }
    catch(error) {
      console.error('Error writing new message to Firebase Database', error);
      return;
    }
}

Prueba el envío de mensajes

  1. Si tu app aún se está publicando, actualízala en el navegador. De lo contrario, ejecuta firebase emulators:start en la línea de comandos para comenzar a entregar la app desde http://localhost:5000 y, luego, ábrela en tu navegador.
  2. Después de acceder, ingresa un mensaje como "¡Hola!" y haz clic en ENVIAR. Con esta acción, el mensaje se escribirá en Cloud Firestore. Sin embargo, aún no verás los datos en tu app web real porque aún debes implementar la recuperación de los datos (en la siguiente sección del codelab).
  3. En Firebase console, puede ver el mensaje nuevo que agregó. Abre la IU de Emulator Suite. En la sección Compilar, haz clic en Base de datos de Firestore (o haz clic aquí y deberías ver la colección messages con el mensaje que acabas de agregar:

6812efe7da395692.png

10. Lea los mensajes

Sincronizar mensajes

Para leer los mensajes en la app, deberás agregar un elemento observable que se active cuando los datos cambien y, luego, crearás un elemento de la IU que muestre mensajes nuevos.

Agregarás código que escucha los mensajes recién agregados desde la app. En este código, recuperarás la instantánea de la colección messages. Solo se mostrarán los últimos 12 mensajes del chat para evitar que se muestre un historial muy largo cuando se cargue.

  1. Regresa al archivo src/app/services/chat.service.ts.
  2. Busca la función loadMessages.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Loads chat message history and listens for upcoming ones.
loadMessages = () => {
  // Create the query to load the last 12 messages and listen for new ones.
  const recentMessagesQuery = query(collection(this.firestore, 'messages'), orderBy('timestamp', 'desc'), limit(12));
  // Start listening to the query.
  return collectionData(recentMessagesQuery);
}

Para escuchar mensajes en la base de datos, crea una consulta en una colección con la función collection para especificar en qué colección se encuentran los datos que quieres escuchar. En el código anterior, escuchas los cambios en la colección messages, que es donde se almacenan los mensajes de chat. También estás aplicando un límite porque solo escuchas los últimos 12 mensajes con limit(12) y ordenas los mensajes por fecha con orderBy('timestamp', 'desc') para obtener los 12 mensajes más recientes.

La función collectionData usa instantáneas de forma interna. Esta se activará cuando se realicen cambios en los documentos que coincidan con la consulta, Esto podría suceder si se borra, modifica o agrega un mensaje. Puedes obtener más información sobre este tema en la documentación de Cloud Firestore.

Prueba la sincronización de mensajes

  1. Si tu app aún se está publicando, actualízala en el navegador. De lo contrario, ejecuta firebase emulators:start en la línea de comandos para comenzar a entregar la app desde http://localhost:5000 y, luego, ábrela en tu navegador.
  2. Los mensajes que creó anteriormente en la base de datos se deben mostrar en la IU de FriendlyChat (consulte a continuación). No dudes en escribir nuevos mensajes; deberían aparecer al instante.
  3. (Opcional) Puedes intentar borrar, modificar o agregar mensajes nuevos de forma manual directamente en la sección Firestore de Emulator Suite. Los cambios deben reflejarse en la IU.

¡Felicitaciones! Estás leyendo documentos de Cloud Firestore en tu app.

angularfire-2.png

11. Enviar imágenes

Ahora, agregarás una función que compartirá imágenes.

Si bien Cloud Firestore es adecuado para almacenar datos estructurados, Cloud Storage es más adecuado para almacenar archivos. Cloud Storage para Firebase es un servicio de almacenamiento de archivos y BLOB, que usarás para almacenar cualquier imagen que un usuario comparta a través de nuestra app.

Guarde imágenes en Cloud Storage

En este codelab, ya agregaste un botón que activa un diálogo del selector de archivos. Después de seleccionar un archivo, se llama a la función saveImageMessage y puedes obtener una referencia al archivo seleccionado. La función saveImageMessage logra lo siguiente:

  1. Crea un mensaje de chat de "marcador de posición" en el feed del chat para que los usuarios vean una animación de "Cargando" mientras subes la imagen.
  2. Sube el archivo de imagen a Cloud Storage en esta ruta de acceso: /<uid>/<file_name>
  3. Genera una URL legible de forma pública para el archivo de imagen.
  4. Actualiza el mensaje de chat con la URL del archivo de imagen recién subido en lugar de la imagen de carga temporal.

Ahora, agregarás la funcionalidad para enviar una imagen:

  1. Regresa al archivo src/chat.service.ts.
  2. Busca la función saveImageMessage.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Saves a new message containing an image in Firebase.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
  try {
    // 1 - You add a message with a loading icon that will get updated with the shared image.
    const messageRef = await this.addMessage(null, this.LOADING_IMAGE_URL);

    // 2 - Upload the image to Cloud Storage.
    const filePath = `${this.auth.currentUser?.uid}/${file.name}`;
    const newImageRef = ref(this.storage, filePath);
    const fileSnapshot = await uploadBytesResumable(newImageRef, file);
    
    // 3 - Generate a public URL for the file.
    const publicImageUrl = await getDownloadURL(newImageRef);

    // 4 - Update the chat message placeholder with the image's URL.
    messageRef ?
    await updateDoc(messageRef,{
      imageUrl: publicImageUrl,
      storageUri: fileSnapshot.metadata.fullPath
    }): null;
  } catch (error) {
    console.error('There was an error uploading a file to Cloud Storage:', error);
  }
}

Prueba enviar imágenes

  1. Si tu app aún se está publicando, actualízala en el navegador. De lo contrario, ejecuta firebase emulators:start en la línea de comandos para comenzar a entregar la app desde http://localhost:5000 y, luego, ábrela en tu navegador.
  2. Después de acceder, haz clic en el botón de carga de imágenes en la parte inferior izquierda angularfire-4.png y selecciona un archivo de imagen con el selector de archivos. Si buscas una imagen, puedes usar esta bonita foto de una taza de café.
  3. Debería aparecer un mensaje nuevo en la IU de la app con la imagen que seleccionaste: angularfire-2.png

Si intentas agregar una imagen sin acceder a tu cuenta, deberías ver un error que te indica que debes acceder para agregar imágenes.

12. Mostrar notificaciones

Ahora, se admitirán las notificaciones del navegador. La app notificará a los usuarios cuando se publiquen nuevos mensajes en el chat. Firebase Cloud Messaging (FCM) es una solución de mensajería multiplataforma que le permite enviar mensajes y notificaciones de forma segura y gratuita.

Agrega el service worker de FCM

La aplicación web necesita un service worker que reciba y muestre las notificaciones web.

El proveedor de mensajería ya debería estar configurado cuando se agregó AngularFire. Asegúrate de que exista el siguiente código en la sección de importaciones de /angularfire-start/src/app/app.module.ts.

provideMessaging(() => {
    return getMessaging();
}),

app/app.module.ts

El service worker solo debe cargar e inicializar el SDK de Firebase Cloud Messaging, que se encargará de mostrar las notificaciones.

Obtén tokens de dispositivo de FCM

Cuando se hayan habilitado las notificaciones en un dispositivo o navegador, obtendrá un token de dispositivo. Este token de dispositivo es el que se usa para enviar una notificación a un dispositivo o navegador en particular.

Cuando el usuario acceda, llamarás a la función saveMessagingDeviceToken. Allí obtendrás el token de dispositivo de FCM del navegador y lo guardarás en Cloud Firestore.

chat.service.ts

  1. Busca la función saveMessagingDeviceToken.
  2. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Saves the messaging device token to Cloud Firestore.
saveMessagingDeviceToken= async () => {
    try {
      const currentToken = await getToken(this.messaging);
      if (currentToken) {
        console.log('Got FCM device token:', currentToken);
        // Saving the Device Token to Cloud Firestore.
        const tokenRef = doc(this.firestore, 'fcmTokens', currentToken);
        await setDoc(tokenRef, { uid: this.auth.currentUser?.uid });
 
        // This will fire when a message is received while the app is in the foreground.
        // When the app is in the background, firebase-messaging-sw.js will receive the message instead.
        onMessage(this.messaging, (message) => {
          console.log(
            'New foreground notification from Firebase Messaging!',
            message.notification
          );
        });
      } else {
        // Need to request permissions to show notifications.
        this.requestNotificationsPermissions();
      }
    } catch(error) {
      console.error('Unable to get messaging token.', error);
    };
}

Sin embargo, este código no funcionará en un principio. Para que tu app pueda recuperar el token de dispositivo, el usuario debe otorgarle permiso para mostrar notificaciones (el siguiente paso del codelab).

Solicita permisos para mostrar notificaciones

Si el usuario aún no le otorgó permiso a la app para mostrar notificaciones, no se te otorgará un token de dispositivo. En este caso, llamarás al método requestPermission(), que mostrará un diálogo del navegador para solicitar este permiso ( en navegadores compatibles).

8b9d0c66dc36153d.png

  1. Regresa al archivo src/app/services/chat.service.ts.
  2. Busca la función requestNotificationsPermissions.
  3. Reemplaza toda la función por el siguiente código.

chat.service.ts

// Requests permissions to show notifications.
requestNotificationsPermissions = async () => {
    console.log('Requesting notifications permission...');
    const permission = await Notification.requestPermission();
    
    if (permission === 'granted') {
      console.log('Notification permission granted.');
      // Notification permission granted.
      await this.saveMessagingDeviceToken();
    } else {
      console.log('Unable to get permission to notify.');
    }
}

Obtén tu token de dispositivo

  1. Si tu app aún se está publicando, actualízala en el navegador. De lo contrario, ejecuta firebase emulators:start en la línea de comandos para comenzar a entregar la app desde http://localhost:5000 y, luego, ábrela en tu navegador.
  2. Después de acceder, debería aparecer el diálogo para permitir las notificaciones: bd3454e6dbfb6723.png.
  3. Haz clic en Permitir.
  4. Abre la Consola de JavaScript de tu navegador. Deberías ver el siguiente mensaje: Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu
  5. Copia el token de dispositivo. Lo necesitarás para la siguiente etapa del codelab.

Cómo enviar una notificación a tu dispositivo

Ahora que tienes tu token de dispositivo, puedes enviar una notificación.

  1. Abre la pestaña Cloud Messaging de Firebase console.
  2. Haz clic en "Nueva notificación".
  3. Ingresa un título y un texto para la notificación.
  4. En el lado derecho de la pantalla, haz clic en "Enviar un mensaje de prueba".
  5. Ingresa el token de dispositivo que copiaste de la consola de JavaScript de tu navegador y, luego, haz clic en el signo más ("+")
  6. Haz clic en "Probar".

Si la app está en primer plano, verás la notificación en la Consola de JavaScript.

Si la app está en segundo plano, debería aparecer una notificación en el navegador, como en este ejemplo:

de79e8638a45864c.png

13. Reglas de seguridad de Cloud Firestore

Consulta las reglas de seguridad de la base de datos.

Cloud Firestore usa un lenguaje de reglas específico para definir los derechos de acceso, la seguridad y las validaciones de datos.

Cuando configuraste el proyecto de Firebase al comienzo de este codelab, elegiste usar las reglas de seguridad predeterminadas del “modo de prueba” para no restringir el acceso al almacén de datos. Puedes ver y modificar estas reglas en Firebase console, en la pestaña Reglas de la sección Base de datos.

Ahora, deberías ver las reglas predeterminadas, que no restringen el acceso al almacén de datos. Esto significa que cualquier usuario puede leer y escribir en cualquier colección de tu almacén de datos.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

Actualizarás las reglas para restringir las opciones con las siguientes reglas:

firestore.rules

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

Las reglas de seguridad deberían actualizarse automáticamente en Emulator Suite.

Consulta las reglas de seguridad de Cloud Storage

Cloud Storage para Firebase usa un lenguaje de reglas específico para definir los derechos de acceso, la seguridad y las validaciones de datos.

Cuando configuraste el proyecto de Firebase al comienzo de este codelab, elegiste usar la regla de seguridad predeterminada de Cloud Storage que solo permite que los usuarios autenticados utilicen Cloud Storage. En Firebase console, en la pestaña Reglas de la sección Storage, puedes ver y modificar reglas. Deberías ver la regla predeterminada que permite que cualquier usuario que haya accedido pueda leer y escribir cualquier archivo en tu bucket de almacenamiento.

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

Deberás actualizar las reglas para hacer lo siguiente:

  • Permitir que cada usuario escriba solo en sus propias carpetas específicas
  • Permite que cualquier persona lea desde Cloud Storage
  • Asegúrate de que los archivos subidos sean imágenes
  • Restringe el tamaño de las imágenes que se pueden subir a 5 MB como máximo

Se puede implementar con las siguientes reglas:

storage.rules

rules_version = '2';

// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

14. Implemente su aplicación con Firebase Hosting

Firebase ofrece un servicio de hosting para entregar tus activos y aplicaciones web. Puedes implementar tus archivos en Firebase Hosting con Firebase CLI. Antes de la implementación, debes especificar en tu archivo firebase.json los archivos locales que se deben implementar. En este codelab, ya hiciste esto porque este paso era necesario para entregar nuestros archivos durante este codelab. La configuración del hosting se especifica en el atributo hosting:

firebase.json

{
  // If you went through the "Cloud Firestore Security Rules" step.
  "firestore": {
    "rules": "firestore.rules"
  },
  // If you went through the "Storage Security Rules" step.
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    "public": "./public"
  }
}

Esta configuración le indica a la CLI que deseas implementar todos los archivos del directorio ./public ( "public": "./public").

  1. Asegúrate de que la línea de comandos acceda al directorio angularfire-start local de tu app.
  2. Ejecuta el siguiente comando para implementar los archivos en el proyecto de Firebase:
ng deploy

Luego, selecciona la opción Firebase y sigue las indicaciones de la línea de comandos.

  1. La consola debería mostrar lo siguiente:
=== Deploying to 'friendlychat-1234'...

i  deploying firestore, storage, hosting
i  storage: checking storage.rules for compilation errors...
✔  storage: rules file storage.rules compiled successfully
i  firestore: checking firestore.rules for compilation errors...
✔  firestore: rules file firestore.rules compiled successfully
i  storage: uploading rules storage.rules...
i  firestore: uploading rules firestore.rules...
i  hosting[friendlychat-1234]: beginning deploy...
i  hosting[friendlychat-1234]: found 8 files in ./public
✔  hosting[friendlychat-1234]: file upload complete
✔  storage: released rules storage.rules to firebase.storage/friendlychat-1234.appspot.com
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendlychat-1234]: finalizing version...
✔  hosting[friendlychat-1234]: version finalized
i  hosting[friendlychat-1234]: releasing new version...
✔  hosting[friendlychat-1234]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com
  1. Visita tu app web, que ahora está alojada por completo en una CDN global con Firebase Hosting en dos de tus propios subdominios de Firebase:
  • https://<firebase-projectId>.firebaseapp.com
  • https://<firebase-projectId>.web.app

También puedes ejecutar firebase open hosting:site en la línea de comandos.

Consulta la documentación para obtener más información sobre cómo funciona Firebase Hosting.

Ve a la sección Hosting de Firebase console de tu proyecto para ver información y herramientas útiles sobre el hosting, como el historial de tus implementaciones, la funcionalidad para revertir a versiones anteriores de tu app y el flujo de trabajo para configurar un dominio personalizado.

15. ¡Felicitaciones!

Utilizaste Firebase para compilar una aplicación web de chat en tiempo real.

Temas abordados

  • Firebase Authentication
  • Cloud Firestore
  • SDK de Firebase para Cloud Storage
  • Firebase Cloud Messaging
  • Firebase Performance Monitoring
  • Firebase Hosting

Próximos pasos

Más información

16. [Opcional] Aplicar la Verificación de aplicaciones con la Verificación de aplicaciones

La Verificación de aplicaciones de Firebase ayuda a proteger tus servicios del tráfico no deseado y a proteger tu backend de los abusos. En este paso, agregarás la validación de credenciales y bloquearás a los clientes no autorizados con la Verificación de aplicaciones y reCAPTCHA Enterprise.

Primero, debes habilitar la Verificación de aplicaciones y reCAPTCHA.

Habilitación de reCAPTCHA Enterprise

  1. En la consola de Cloud, busca y selecciona reCaptcha Enterprise en Seguridad.
  2. Habilita el servicio como se solicita y haz clic en Crear clave.
  3. Ingresa un nombre visible cuando se solicite y selecciona Sitio web como el tipo de plataforma.
  4. Agrega las URLs implementadas a la Lista de dominios y asegúrate de que la opción “Usar el desafío de la casilla de verificación” no esté seleccionada.
  5. Haz clic en Crear clave y guarda la clave generada en algún lugar para guardarla de forma segura. Lo necesitarás más adelante en este paso.

Habilita la Verificación de aplicaciones

  1. En Firebase console, busca la sección Build en el panel izquierdo.
  2. Haz clic en Verificación de aplicaciones y, luego, en la pestaña Método de acceso para navegar a Verificación de aplicaciones.
  3. Haz clic en Register, ingresa tu clave de reCAPTCHA Enterprise cuando se te solicite y, luego, haz clic en Guardar.
  4. En la vista de APIs, selecciona Storage y haz clic en Aplicar. Haz lo mismo con Cloud Firestore.

Ahora se debe aplicar la Verificación de aplicaciones. Actualiza la app y trata de ver o enviar mensajes de chat. Deberías recibir el siguiente mensaje de error:

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

Esto significa que la Verificación de aplicaciones bloquea las solicitudes no validadas de forma predeterminada. Ahora, agreguemos validación a tu app.

Navega al archivo environment.ts y agrega reCAPTCHAEnterpriseKey al objeto environment.

export const environment = {
  firebase: {
    apiKey: 'API_KEY',
    authDomain: 'PROJECT_ID.firebaseapp.com',
    databaseURL: 'https://PROJECT_ID.firebaseio.com',
    projectId: 'PROJECT_ID',
    storageBucket: 'PROJECT_ID.appspot.com',
    messagingSenderId: 'SENDER_ID',
    appId: 'APP_ID',
    measurementId: 'G-MEASUREMENT_ID',
  },
  reCAPTCHAEnterpriseKey: {
    key: "Replace with your recaptcha enterprise site key"
  },
};

Reemplaza el valor de key con tu token de reCAPTCHA Enterprise.

Luego, navega al archivo app.module.ts y agrega las siguientes importaciones:

import { getApp } from '@angular/fire/app';
import {
  ReCaptchaEnterpriseProvider,
  initializeAppCheck,
  provideAppCheck,
} from '@angular/fire/app-check';

En el mismo archivo app.module.ts, agrega la siguiente declaración de variable global:

declare global {
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}

@NgModule({ ...

En las importaciones, agrega la inicialización de la Verificación de aplicaciones mediante ReCaptchaEnterpriseProvider y establece isTokenAutoRefreshEnabled en true para permitir que los tokens se actualicen automáticamente.

imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAppCheck(() => {
const appCheck = initializeAppCheck(getApp(), {
  provider: new ReCaptchaEnterpriseProvider(
  environment.reCAPTCHAEnterpriseKey.key
  ),
  isTokenAutoRefreshEnabled: true,
  });
  if (location.hostname === 'localhost') {
    self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
  }
  return appCheck;
}),

Para permitir pruebas locales, establece self.FIREBASE_APPCHECK_DEBUG_TOKEN en true. Cuando actualices la app en localhost, se registrará un token de depuración en la consola, similar al siguiente:

App Check debug token: CEFC0C76-7891-494B-B764-349BDFD00D00. You will need to add it to your app's App Check settings in the Firebase console for it to work.

Ahora, ve a Vista de apps de la Verificación de aplicaciones en Firebase console.

Haz clic en el menú ampliado y selecciona Administrar tokens de depuración.

Luego, haz clic en Agregar token de depuración y pega el token de depuración de tu consola como se te solicite.

Navega al archivo chat.service.ts y agrega la siguiente importación:

import { AppCheck } from '@angular/fire/app-check';

En el mismo archivo chat.service.ts, inserta la Verificación de aplicaciones junto con los otros servicios de Firebase.

export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...

¡Felicitaciones! Ahora debería funcionar la Verificación de aplicaciones en su aplicación.