Hace unos años tuve que idear una solución a una necesidad que Angular o cualquier SPA no soporta (por sí solo) y te compartiré mi propuesta.
Aviso: Si apenas conoces Angular, este artículo podría ser muy avanzado para tí; más adelante explicaré estos conceptos por separado y a profundidad.
Situación en ese entonces
Le daba mantenimiento a un sitio desarrollado con Angular, el sitio registraba los diversos datos del usuario en una interfaz de estilo wizard.
¿Qué es un Wizard?
Wizard (en este contexto) es un estilo de interfaz, que te va guiando a partir de una serie de pasos o condiciones, que debe completar el usuario para lograr un objetivo; en pocas palabras, next, next, next ...
Necesidad
Se me pidio capturar el último paso en el que quedo el usuario y así pudiera avanzar en otro momento; para ilustrar este caso muestro la siguiente imagen:
Digamos que tengo 3 pasos: Producto, Datos Personales y Dirección; en cada uno de ellos hay unos formularios que rellenar antes de pasar al siguiente.
Esto es más facil resolverlo con un stack que me permita alojar sesiones como un clásico MVC , pero ya que mi cliente es necio y quiere Angular, pues a romperse la cabeza
Caso de uso #1
- El usuario completa el formulario de Producto
- El usuario da clic a siguiente
- El usuario ve la pantalla de datos personales, le da pereza y cierra
- El usuario mientras desayuna se acuerda e ingresa denuevo
En este caso el sitio debe redireccionar al usuario a la última pantalla que quedó, es decir Datos Personales
Caso de uso #2
- Un usuario A le comparte el sitio a un usuario B
- El usuario B ingresa a la pantalla de Datos Personales
En este caso el sitio debe redireccionarte al inicio del flujo para que ingreses tus credenciales
Hay muchos más casos, pero me extendería demasiado.
Hay algo que no te estoy contando...
¿Cómo sabe la aplicación quién eres y en que paso quedaste? Necesitas autenticación, debes tener una puerta inicial (login), en donde se verifique las credenciales, código único u otro para poder darle acceso a esa persona.
El AuthGuard clásico
Si ya has hecho sitios con Angular o en algún otro ecosistema, sabes que debes proteger las rutas, esto se conoce como "rutas protegidas". Consiste en verificar un token o algún estado único para saber que hay alguien autorizado viendo la página.
Para lograr esto en Angular, se utiliza el clásico AuthGuard que verifica si hay un token o algun estado previamente actualizado al entrar por un login o una puerta principal. Ahora mi ejemplo evolucionaria a esto:
Los Guards (como el nombre indica) protegen las rutas a partir de una condición y un AuthGuard te redirecciona hacia la pantalla de inicio de sesión; si no cumple la condición.
¿Cómo este Guard solventa el problema?
Digamos que tu API te entrega un token JWT al iniciar sesión:
- Guardas ese token en el localstorage
- Cambias el estado de la variable "IsLoggedIn a true" para registrar de que se ha iniciado sesión
- Ahora el AuthGuard pregunta por IsLoggedIn y verifica que se inició sesión previamente cada vez que pasa a una página protegida.
¿Qué pasa si alguien abre el localstorage e ingresa 123 como token?
Si estoy guardando un token, es porque enviaré ese token en un header Bearer más adelante y ese API verificará la procedencia de ese token; cuando ocurra eso seguro saltará un bonito 401.
Si aún así deseas verificar el token, los Guards permiten resolver un Observable; es decir, que podrías ejecutar un servicio que te lo confirme, obviamente tardará más la pantalla.
No desencriptes el JWT, ni desactives el clic derecho; no tiene sentido alguno y solo le provocarás un dolor de cabeza al que deba mantener tu sitio. Tal vez arme un artículo de malas prácticas más adelante.
Y a la final como se guarda para después
Teniendo el mecanismo de AuthGuard e inicio de sesión, ¡Ahora si podemos tocar este punto!
El guardado para después lo resolví sincronizando una numeración para cada pantalla, igual funcionaría con algún valor único obviamente.
Luego cada vez que inicias sesión y pasas por una pantalla ejecutas una función de redirección para verificar el último paso en el quedó el usuario, ese es el concepto.
Te explico mi solución
Utilizo un AuthGuard que solo verifica si hay un token en el localstorage, hasta ahí llega mi Guard.
Para verificar el token y saber donde quedó el usuario utilizo Resolvers. Un Resolver tiene el objetivo de obtener información antes de que se muestre la pantalla, cosas como el llamado de catálogos se puede realizar aquí.
Primero se ejecutan los Guards y luego los Resolvers
Ya sabiendo todo esto, lo que hago en cada Resolver es lo siguiente:
- Ejecuto un servicio para verificar el token único
- Verifico el paso atado a la pantalla (lo explicaré más adelante)
- Ejecuto la redirección si es necesario
- Realizo la llamada de todos los servicios necesarios en pantalla
Podría utilizar Guards, pero utilizo resolvers por temas de diseño y conveniencia:
Diseño: Como mencioné anteriormente, agregaría demora al proceso de autorización en la pantalla y hay pantallas que no necesitan una verificación tan compleja. De este modo los AuthGuards son muy reutilizables y los Resolvers se mantienen únicos para cada pantalla.
Conveniencia: Hay veces que mi backend es flojo y solo me da un servicio para verificar y darme el perfil (con el paso correspondiente).
Ahora como veamos denuevo los casos y como se resuelve con la implementación anterior:
- Si inicias sesión
- Obtiene el perfil con la numeración
- Utiliza la función de Redirección
- Te redirecciona a Dirección
- Si el usuario tiene un token, ingresa por Datos Personales y realmente debe estar en Dirección:
- El AuthGuard te permitirá pasar
- Obtiene el perfil con la numeración
- Utiliza la función de Redirección
- Te redirecciona a Dirección
- Si el usuario estaba en Datos Personales y refresca la pantalla
- El AuthGuard te permitirá pasar
- Obtiene el perfil con la numeración
- Utiliza la función de Redirección
- Te redirecciona a Datos Personales INFINITO
Cuidado con este último punto, debes agregar una condición para que no entre a la función de redirección; si ya está donde deberías estar, segun el paso que vas a recibir del perfil.
Desventaja
Ya no tendrás el control total de tu flujo, dependerá del API.
Esto será un "API for frontend", un API totalmente diseñado solo para funcionar para tu frontend.