Autenticación
Servicio de autenticación (AuthService)
Dentro de la carpeta de autenticación (auth), lo primero que haremos será un servicio llamado AuthService donde gestionaremos la autenticación de los usuarios (login, registro, logout, y comprobación del token). Tendremos además, una propiedad de tipo signal (boolean) llamada logged que indicará si el usuario está logueado o no, y al ser una señal permitirá reaccionar a los cambios de valor fácilmente.
Para almacenar el token en el almacenamiento local, instalaremos el plugin @capacitor/preferences, de forma que en dispositivos móviles no utilice localStorage, ya que la persistencia no está garantizada, sino los mecanismos nativos de cada sistema (Android, iOS) como vimos en la sección Capacitor del curso.
npm i @capacitor/preferences
ng g i auth/interfaces/user
ng g i auth/interfaces/responses
ng g service auth/services/auth
En el servicio AuthService hay ciertas diferencias respecto a una aplicación Angular de navegador al utilizar Preferences (promesas) en lugar de LocalStorage (síncrono) para gestionar el token de autenticación:
- En el método login el servidor devuelve un token que procesaremos guardándolo en Preferences. Al trabajar Preferences con promesas, para que no devuelva un dato de tipo Observable<Promise<void>>, utilizamos switchMap, que permite cambiar el resultado del observable inicial al resultado de otro observable o promesa, en este caso de la función async donde guardamos el token que devuelve una promesa.
- Pasa algo similar con el método isLogged, ya que tenemos que obtener el token primero (promesa) para saber si existe y llamar al servicio que comprueba el token (observable). En este caso hemos transformado la promesa de Preferences.get a observable con la función from. Utilizamos switchMap para intercambiar el resultado de este observable por el de la llamada al servidor.
Interceptores
A continuación crearemos 2 interceptores, uno llamado baseUrl que inyectará en todas las peticiones al servidor la ruta del mismo y otro llamado auth que si está presente, agregará a la petición la cabecera Authorization con el token de acceso.
ng g interceptor interceptors/base-url
ng g interceptor interceptors/auth
Finalmente registramos los interceptores junto al servicio HttpClient en el archivo main.ts.
Guardianes de rutas
Como es útil tener un mecanismo para controlar a qué páginas de la aplicación puede acceder o no un usuario logueado, crearemos 2 guardianes (guards) de tipo CanActivate para las rutas. Uno llamado login-activate que permitirá acceder a una ruta solo cuando el usuario está logueado, y otro llamado logout-activate para lo contrario, permitir acceder a rutas como el login o registro solo a usuarios no autenticados.
ng g guard guards/login-activate
ng g guard guards/logout-activate
Finalmente, activamos los guardianes en el archivo de rutas principales de la aplicación (app.routes.ts).
Menú lateral
Vamos a quitar lo que no necesitemos y hacer unas pequeñas modificaciones en el menú lateral. El menú lateral está en app.component.html (componente ion-menu). Después quitaremos el encabezado del menú (ion-list-header, ion-note) para poner en su lugar un elemento ion-item al principio del menú que muestre los datos del usuario logueado.
Además, podemos quitar el array de labels y la lista que los muestra, ya que no tienen ninguna funcionalidad en nuestro ejemplo. En la lista de enlaces del menú, dejaremos solo uno por ahora: /products (quita también todas las importaciones de iconos excepto la de home). Sobre los iconos, en la plantilla vamos a quitar la distinción entre Android (md) y iOS (ios) y pondremos siempre el mismo icono usando [name] para simplificar. De esta manera no tenemos que importar la versión outline y sharp de cada icono que usemos.
En las páginas de login y registro (usuario no logueado) vamos a deshabilitar el menú (propiedad disabled) para que no aparezca ni haciendo el gesto con el dedo (desplazamiento a la derecha), ya que aunque no pongamos botón de menú, si este está activo, puede aparecer con este gesto. Vamos a vincular que el menú esté habilitado a que los datos del usuario autenticado estén disponibles
Haremos una función effect vinculada a la signal logged del servicio AuthService, y que cada vez que cambie obtendremos los datos del usuario (si está logueado), o lo pondremos a null si se ha desconectado para que se desactive el menú.
Otra cosa que vamos a tener en cuenta es la utilización de los plugins de Capacitor para ocultar la imagen SplashScreen y cambiar el color de la barra de notificaciones nativa en cuanto la aplicación esté preparada (Platform.isReady).
npm i @capacitor/status-bar
npm i @capacitor/splash-screen
Página de login
Para crear una página para gestionar el login de usuarios, utilizaremos el siguiente comando:
ionic g page auth/login
A continuación añadimos la ruta a la página de login en el archivo de rutas auth.routes.ts:
Ionic, al menos en la versión 8, cuando creas un nuevo componente o página, importa el módulo de Angular CommonModule, que incluye directivas como ngClass, ngIf, etc. Por eficiencia, vamos a quitar esa importación, y ya importaremos si necesitamos, las directivas correspondientes por separado.
Por ahora no redirigiremos a la ruta /products hasta que la implementemos.

No hay que olvidar registrar los iconos necesarios, en este caso logIn y documentText en AppComponent.
Página de registro
A continuación crearemos una página para dar de alta nuevos usuarios. Esta página tendrá un formulario con los datos de un usuario y cuando el registro se complete con éxito, redirigirá a la página de login.
ionic g page auth/register
Después añadimos la ruta a la página de registro en el archivo de rutas auth.routes.ts:
Como en la página de login, quitaremos la importación de CommonModule.
Para elegir la imagen de avatar, utilizaremos la cámara, por lo que instalaremos el plugin de Capacitor correspondiente (camera) junto a pwa-elements para poder probarlo desde el navegador. Para habilitar los componentes PWA de Ionic debemos registrarlos en el archivo main.ts llamando a defineCustomElements(window).
npm i @capacitor/camera
npm i @ionic/pwa-elements
En esta página pondremos un formulario con la información del usuario que vamos a registrar. Crearemos también un validador para comprobar si los campos de la contraseña tienen el mismo valor. Si no queremos tener que utilizar el prefijo app en el selector, debemos dejarlo a cadena vacía tanto en el archivo angular.json (propiedad "prefix") como en la configuración de ESLint (.eslintrc.json).
ng g d validators/valueEquals
El método registerOnValidatorChange permite a Angular registrar una función que podemos llamar cada vez que cambie un valor que reciba la directiva (Input) para que vuelva a validar el input que contiene el validador. Si no, la validación solo se recalcular cuando se edita el valor del campo, por lo que si tenemos 2 contraseñas iguales y luego modificamos el primer campo para que sean diferentes, no nos marcaría el segundo campo como invalid.

Importante: No te olvides de importar y registrar los iconos arrowUndoCircle, camera, images, y checkmarkCircle en app.component.ts.
Al usar una aplicación zoneless, como la obtención de la imagen en base64 es una operación asíncrona, Angular no detectará los cambios posteriores a dicha operación por defecto. Como modificamos una propiedad interna del objeto, es más sencillo utilizar el servicio ChangeDetectorRef para avisar a Angular de los cambios que envolver los datos del usuario en una signal.