Que Angular es un framework muy extendido para escribir aplicaciones web y móviles es un hecho. Si además lo combinamos con Firebase, tenemos un potente entorno de desarrollo rápido de aplicaciones que permiten ahorrarte mucho tiempo por varios motivos:
- Puedes centrarte en la experiencia de usuario y olvidarte del backend, ya que este está integrado en los servicios que te ofrece Firebase.
- Dispones de una base de datos en tiempo real, por lo que cualquier cambio en los datos se verá reflejado automáticamente en tu aplicación sin tener que recargar o volver a consultar.
- Tienes todo el flujo de autentificación de usuario listo para implementar.
Por todo esto y más, creo que Angular + Firebase es una buena elección para empezar a programar aplicaciones profesionales y escalables.
Y aunque todo esto está muy bien, hay una tarea que siempre me parece repetitiva y monótona a la hora de empezar una nueva aplicación, y esta es, la implementación del flujo de autentificación de usuario. Este flujo siempre suele ser el mismo en todas las aplicaciones:
- Registro de usuario
- Login de usuario
- Detección de estado de la autentificación en las vistas.
En este artículo daremos los primeros pasos creando una aplicación desde cero con autentificación integrada. Vamos a aprender a configurar el entorno de desarrollo de Angular con Angular CLI, a crear componentes y páginas, a gestionar el routing, a integrar nuestro frontend Angular con el backend de Firebase y por último a gestionar y supervisar la autentificación de usuario… ¡Para que no tengas que volver a hacerlo más! Empecemos.
1. Instalación de la CLI de Angular
Para poder crear un proyecto de Angular necesitamos instalar en nuestro equipo la CLI (Command Line Interface) de Angular. Esta herramienta nos sirve para crear proyectos, componentes y ejecutar nuestro frontend de manera local, entre otras cosas. Para instalarla y tenerla disponible de manera global en nuestro sistema, debemos ejecutar el siguiente comando en nuestro terminal:
$ npm install -g @angular/cli
Una vez tenemos instalada la CLI ya ejecutamos el siguiente comando para crear un proyecto Angular vacío. A la pregunta de si queremos usar el routing de Angular, contestamos que sí. Luego nos pregunta qué preprocesador de CSS usar. A mi me gusta SCSS, pero ahora no es relevante.
$ ng new myproject ? Would you like to add Angular routing? Yes ? Which stylesheet format would you like to use? CSS ❯ SCSS [ https://sass-lang.com/documentation/syntax#scss ] Sass [ https://sass-lang.com/documentation/syntax#the-indented-syntax ] Less [ http://lesscss.org ] Stylus [ http://stylus-lang.com ]
Ya tenemos un proyecto de Angular vacío preparado para funcionar. Para lanzar un servidor web con nuestro proyecto sólo tendremos que entrar en el directorio que se acaba de crear y ejecutar npm start:
$ cd myproject $ npm start
Cuando acabe de “compilar” tendremos un servidor web en http://localhost:4200 con nuestro nuevo proyecto al que podremos acceder desde nuestro navegador. Te recomiendo que dejes un terminal abierto con npm start para que Angular vaya recargando automáticamente los cambios que vayamos haciendo. Lo que veremos entrando en http://localhost:4200 será algo parecido a esto:
El código de esta vista se encuentra en src/app/app.component.html. Puedes curiosear un poco, pero básicamente hay que tirarlo todo a la basura. Simplemente se trata de una pequeña plantilla de bienvenida para que el proyecto no se vea vacío.
2. Componentes
En Angular vas a trabajar con unas entidades que se llaman componentes. Un componente es básicamente cualquier cosa que se vea en la pantalla que tenga una organización propia, desde un botón con sus eventos “click” hasta una página entera. Es importante tener claro que los componentes pueden contener otros componentes.
En el proyecto base de Angular existe un único gran componente llamado AppComponent y su vista está descrita en src/app/app.component.html. A no ser que nuestra aplicación sea extremadamente sencilla, vamos a necesitar crear más de una página y dividir nuestra aplicación en varios componentes grandes (o páginas). Por este motivo hemos activado el routing. También por este motivo vamos a eliminar todo el código de src/app/app.component.html y vamos a dejar sólo las siguientes líneas:
<h1>Our first Angular app</h1> <router-outlet></router-outlet>
Lo que acabamos de hacer es decirle a la vista principal de nuestra aplicación que debe mostrar una frase y, justo debajo, el contenido del componente activo en nuestro servicio de routing. Veremos como funciona el Router de Angular en el siguiente punto, pero antes de eso vamos a crear dos páginas o grandes componentes, uno para el login y otro para el dashboard de usuario.
$ ng g component LoginPage CREATE src/app/login-page/login-page.component.scss (0 bytes) CREATE src/app/login-page/login-page.component.html (25 bytes) CREATE src/app/login-page/login-page.component.spec.ts (650 bytes) CREATE src/app/login-page/login-page.component.ts (291 bytes) UPDATE src/app/app.module.ts (489 bytes) $ ng g component DashboardPage CREATE src/app/dashboard-page/dashboard-page.component.scss (0 bytes) CREATE src/app/dashboard-page/dashboard-page.component.html (29 bytes) CREATE src/app/dashboard-page/dashboard-page.component.spec.ts (678 bytes) CREATE src/app/dashboard-page/dashboard-page.component.ts (307 bytes) UPDATE src/app/app.module.ts (601 bytes)
2. Routing
Ya tenemos dos páginas, una para el login y otra para el dashboard. Vamos a ver de que manera podemos acceder a ellas mediante el routing de Angular. El routing es el mecanismo que tiene Angular para construir las diferentes URL de nuestra aplicación y su funcionamiento se programa en src/app/app-routing.module.ts. Lo que vamos a hacer ahora es añadir dos nuevas rutas para nuestras dos nuevas páginas. Sólo hay que añadir los objetos correspondientes en el array routes asociándolas a nuestros nuevos componentes:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LoginPageComponent } from './login-page/login-page.component'; import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; /* Add here your routes */ const routes: Routes = [ { path: 'login', component: LoginPageComponent }, { path: 'dashboard', component: DashboardPageComponent }, ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Si en este momento abrimos nuestro navegador en http://localhost:4200/login veremos esto:
En primer lugar vemos la frase “Our first Angular app”, que como podéis comprobar forma parte de la vista del componente general AppComponent, descrita en src/app/app.component.html. Más abajo vemos “login-page works!” que es la vista de nuestro nuevo componente LoginPageComponent descrita en src/app/login-page/login-page.component.html. De este modo es como trabaja el router de Angular. Generalmente el componente principal AppComponent se encarga de dibujar los elementos comunes a todas las páginas. La etiqueta <router-outlet> se encarga de dibujar el contenido de los componentes activados por nuestro router.
3. Firebase
Antes de continuar, y como ya necesitamos meternos en los temas de autentificación de usuarios, vamos a crear nuestro backend en la consola de Firebase. Para ello necesitamos aprovisionar un proyecto y una aplicación web dentro del mismo. Una vez hecho esto, copiaremos sus credenciales para insertarlas en nuestro proyecto Angular:
Después de esto debemos instalar el SDK de Firebase en nuestro proyecto Angular. Podríamos instalar simplemente el SDK para web que nos indican en la documentación oficial de Firebase, pero creo que es mucho más útil instalar directamente la librería oficial de Firebase para Angular, AngularFire, y seleccionar el proyecto que acabamos de crear en la consola de Firebase:
$ ng add @angular/fire@next Installing packages for tooling via npm. Installed packages for tooling via npm. UPDATE package.json (1534 bytes) ✔ Packages installed successfully. ✔ Preparing the list of your Firebase projects ? Please select a project: (Use arrow keys or type to search) ❯ myproject (myproject-a2e0b)
Ahora que ya tenemos nuestro backend Firebase aprovisionado y la librería oficial para Angular instalada, debemos configurarla en nuestro proyecto para que queden definitivamente enlazados. Para ello necesitamos hacer unas modificaciones en nuestro módulo de aplicación. Recuerda que debes copiar TU PROPIA configuración de firebaseConfig desde tu consola Firebase:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppRoutingModule } from './app-routing.module'; import { AppComponent } from './app.component'; import { LoginPageComponent } from './login-page/login-page.component'; import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; import { AngularFireModule } from '@angular/fire'; import { AngularFireAuthModule, AngularFireAuth } from '@angular/fire/auth'; /* REPLACE WITH YOUR CONFIG */ const firebaseConfig = { apiKey: 'AIzaSyCOZ_rGQGQjrw-iB9yXqIZasD4PRqY****', authDomain: 'myproject-a2e0b.firebaseapp.com', databaseURL: 'https://myproject-a2e0b.firebaseio.com', projectId: 'myproject-a2e0b', storageBucket: 'myproject-a2e0b.appspot.com', messagingSenderId: '80332117****', appId: '1:803321171754:web:e779ee1f1bc939e50c****', measurementId: 'G-VJF2E1****' }; @NgModule({ declarations: [ AppComponent, LoginPageComponent, DashboardPageComponent ], imports: [ BrowserModule, AppRoutingModule, AngularFireModule.initializeApp(firebaseConfig), AngularFireAuthModule ], providers: [ AngularFireAuth ], bootstrap: [AppComponent] }) export class AppModule { }
En este punto es mejor que pares la ejecución de tu proyecto en tu terminal con Ctrl+C y vuelvas a “compilar” con npm start. ¡Ahora nuestro proyecto Angular ya está integrado con nuestro backend Firebase!
3. Autentificación
No cantes victoria. Aunque tengamos nuestro backend integrado, podemos entrar indiscriminadamente a /login o a /dashboard dado que no hay ninguna comprobación del usuario en ninguna parte. Aun no estamos comprobando la autentificación.
Antes de nada debemos decirle a nuestro backend qué tipo de autentificación vamos a soportar en nuestra aplicación. Para este ejemplo vamos a activar la autentificación mediante Google, pero puedes activar las que necesites.
Ahora vamos a modificar nuestra página /login para añadir un botón que abra el diálogo de autentificación con Google.
<p>login-page works!</p> <button (click)="loginGoogle()">Login with Google</button>
import { Component, OnInit } from '@angular/core'; import { AngularFireAuth } from '@angular/fire/auth'; import * as firebase from 'firebase'; import { Router } from '@angular/router'; @Component({ selector: 'app-login-page', templateUrl: './login-page.component.html', styleUrls: ['./login-page.component.scss'] }) export class LoginPageComponent implements OnInit { constructor( private afAuth: AngularFireAuth, private router: Router ) { } ngOnInit(): void { } loginGoogle() { this.afAuth.signInWithPopup(new firebase.auth.GoogleAuthProvider()) .then(userCredentials => { /* LOGIN OK */ this.router.navigateByUrl('/dashboard'); }) .catch(err => { /* LOGIN ERR */ alert(err.message); }) .finally(() => { /* DO SOMETHING IN ANY CASE */ }); } logout() { this.afAuth.signOut(); } }
En este momento veremos el botón Login with Google en nuestra vista http://localhost:4200/login. Si hacemos click nos aparecerá una ventana pidiéndonos nuestra cuenta de Google para identificarnos. En caso de que nos identifiquemos correctamente le decimos al router que nos envíe a /dashboard. Si la identificación falla mostramos el mensaje de error devuelto por Google. Este ejemplo es extrapolable a cualquier método de identificación soportado por Firebase.
Ya tenemos la mitad de nuestro flujo de autentificación hecho, pero… ¿Qué pasa si vamos directamente a la ruta /dashboard sin pasar por el login primero? Abre una ventana de navegación privada para empezar una nueva sesión limpia de usuarios y lo comprobarás:
Efectivamente, entramos a nuestro dashboard tranquilamente sin estar autentificados. Y eso es malo, por que ningún usuario sin autentificar debería poder visitar nuestro dashboard. Por lo tanto vamos a poner las medidas de seguridad necesarias para que los usuarios no autentificados no puedan husmear donde no deben. Para ello vamos a usar los mecanismos de AngularFire diseñados para esta tarea. Esto es la clase AngularFireAuthGuard y sus funciones asociadas. Se implementan en nuestro módulo de routing:
import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; import { LoginPageComponent } from './login-page/login-page.component'; import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; import { AngularFireAuthGuard, redirectUnauthorizedTo, redirectLoggedInTo } from '@angular/fire/auth-guard'; const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']); const redirectLoggedInToDashboard = () => redirectLoggedInTo(['dashboard']); const routes: Routes = [ { path: 'login', component: LoginPageComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectLoggedInToDashboard } }, { path: 'dashboard', component: DashboardPageComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin } } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Como véis he extendido las propiedades de las rutas con los parámetros canActivate y data. De esta manera es AngularFireAuthGuard quien se ocupa de comprobar si el usuario ya está debidamente autentificado. Mediante el parámetro data configuramos la acción a realizar. En este caso los usuarios no autentificados que intenten ir a /dashboard los vamos a redirigir a /login, mientras que los usuarios autentificados que entren en /login los vamos a mandar directamente a /dashboard. Haz la prueba.
Es una gran ventaja para el programador poder despreocuparse de la gestión y supervisión de la autentificación, y desde este momento ya puedes hacerlo. Simplemente deberás añadir nuevos guards a las nuevas páginas.
Vamos a rizar el rizo, ya que no hemos contemplado la ruta más importante de nuestra aplicación: la ruta raíz. O lo que es lo mismo, la página de inicio: http://localhost:4200/. Personalmente me gusta apoyar toda esta lógica en el componente de login. De esta manera la página de inicio es la que decide hacia dónde debe ir la ruta o simplemente mostrar la pantalla de login en caso de no tener al usuario atentificado. Para ello simplemente deberemos modificar la ruta del LoginPageComponent por la ruta raíz dejando la propiedad path vacía:
[...] const routes: Routes = [ { path: '', component: LoginPageComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectLoggedInToDashboard } }, [...]
Como podrás comprobar en http://localhost:4200, ¡Nuestro sistema de autentificación está funcionando ya correctamente! Si no estamos autentificados nos muestra la vista de login, y si estamos autentificados nos envía al dashboard. Si intentamos visitar el dashboard sin estar autentificados, nos envía de nuevo a login. Fácil, y para toda la familia.
En este punto tenemos todo lo necesario para empezar a implementar nuestra interfaz de usuario y nuestra lógica de negocio sin preocuparnos más por la autentificación de usuario. Este es un buen momento para guardar tu proyecto y usarlo como plantilla para tus futuros desarrollos.
Repasemos lo que hemos aprendido:
- Hemos instalado la CLI de Angular, herramienta indispensable para crear proyectos Angular.
- Hemos creado componentes de página y los hemos enlazado a sus rutas correspondientes mediante el módulo Router de Angular.
- Hemos aprovisionado un backend de Firebase y lo hemos enlazado con nuestro frontend Angular mediante la librería oficial de Firebase para Angular.
- Hemos dado de alta un método de autentificación de usuarios, y lo hemos implementado usando AngularFireAuth.
- Hemos supervisado y protegido las rutas de nuestra aplicación usando AngularFireAuthGuard.
- Hemos configurado nuestra página de inicio.
En próximas entregas:
- Implementaremos la librería de componentes PrimeNG para hacer más bonita y útil la interfaz de usuario.
- Usaremos Firebase Database para consultar y modificar datos en tiempo real.
- Desplegaremos nuestra aplicación Angular / Firebase en producción usando el hosting de Firebase.
Enlaces útiles:
- Documentación oficial de Firebase para web.
- Documentación oficial de AngularFire.
- Angular Firebase Template, mi plantilla para empezar proyectos Angular con Firebase y PrimeNG. Implementa casi todo lo que hemos comentado en este artículo, y alguna cosa más.
Si te ha sido de utilidad este artículo, tal vez lo sea para otra persona de tu entorno. ¡Compártelo en tus redes!