De idea a proyecto - Parte 2 - Autenticación

Cover image

Bienvenido a "De idea a proyecto", una serie de posts en los que voy a compartir y documentar mi experiencia construyendo un proyecto que tengo en mente, espero que lo encuentre interesante y que podamos aprender un poco.

No sé en cuántos proyectos he trabajado, pero estoy seguro de que en todos ellos ha sido necesario implementar alguna solución para la autenticación. Es un trabajo que se vuelve hasta repetitivo y aburrido. Casi siempre es el mismo formulario, con los mismos campos, con las mismas validaciones, con los mismos redireccionamientos y mensajes de error.

Bueno en realidad no es tan simple, la parte de registro de una aplicación es una de las más importantes, ya que dependiendo de qué tan fácil sea la experiencia para el usuario, más capacidad de retención vas a tener. Además de la percepción de la seguridad es posible que los usuarios no vayan a confiar cierta información.

En el pasado he desarrollado login sencillos, pero luego se vuelve un poco más complicado, hay distintas formas de guardar una sesión, se puede hacer en el cliente, se puede hacer en el servidor, se puede usar JWT… En fin, es diverso y repetitivo, hay plantillas de proyectos que ya tienen un login y manejan la parte de verificación de dirección de correo electrónico. Pero esta vez quería hacer algo distinto.

Desde hace años vengo viendo cómo en algunas aplicaciones se puede hacer login con terceras partes como facebook, google, twitter… Y me hizo pensar en varios beneficios.

  • La gente no quiere recordar otra contraseña para otro sitio.
  • La gente no siempre va a confiar sus credenciales a cualquier sitio.
  • El manejo de la autenticación se lo podemos dejar a terceras partes sin preocuparnos por eso.
  • Es una oportunidad para quitarme la duda y aprender algo nuevo (ya lo he usado en el pasado pero es la primera vez que lo hago en producción, además hay distintos flujos y spoiler, yo necesitaba uno para SSR).

Pero, ¿En dónde empiezo?

Bueno la verdad es que pasé buscando por bastante tiempo, pero hay diversas soluciones. Lo que pasa es que antes de implementarlas tenía que aprender varias cosas básicas.

  • Tener claro el stack de mi aplicación o al menos saber si va a ser SSR o SPA…
  • Conocer lo básico sobre qué es Open Authentication, cómo funciona, y cuáles son los flujos disponibles.
  • La posibilidad de tener que hacer más trabajo con terceras partes en caso de que tuviera distintos OAuth providers, ya que tienes que obtener llaves públicas y privadas o client ids de sus plataformas, y si tienes varios ambientes puede ser tedioso manejarlas todas. No sólo eso, si no que hubiera tenido que leer su documentación una por una para ver cómo implementar el servicio.

Sabía que iba a usar Nuxt en mi aplicación con SSR, tomé el tiempo para conocer los flujos de OAuth, y supe que el que me iba a servir era Authorization Code Flow.

Ahora sabiendo esto, había una herramienta bastante prometedora que pensé en usar que se llama passport.js, es muy buena porque ya no me tendría que preocupar por implementar las estrategias manualmente, si no que ellos tienen una galería de estrategias muy extensa, en la que puedes instalar el paquete, ya sea facebook, google, twitter… Y nada más debes obtener las llaves y usar las estrategias, así que ya sonaba lo suficientemente fácil, pero aún se puede poner más fácil.

Luego encontré la mejor forma de Autenticación: Auth0

Auth0 viene a solucionar todos los problemas de autenticación, estamos hablando de una empresa que se dedica a esto así que es mejor dejárselo a los expertos. Tienen implementaciones para todos los flujos de OAuth, junto con una guía o proyecto de ejemplo para cada tecnología popular, SPAs con Vue o React, SSR con PHP, Next y Nuxt… Por lo que empezar no iba a ser difícil, ellos proveen las llaves para poder usarlo y conectarlo con otros OAuth providers como Facebook, para que no tenga que ir a obtener sus llaves específicas y para distintos ambientes. Además ofrece la capacidad de customizar la pantalla de login y sus campos, tiene hooks para cuando pasan determinados eventos, entre muchas otras features. En fin, la herramienta es muy buena y la voy a seguir usando en todas mis apps.

El reto de la semana

OAuth es perfecto, pero para la fecha en que yo empecé a construir mi proyecto, no había una forma que me sirviera para poder guardar información adicional del usuario con mi base de datos.

  • Podía usar sus hooks ya que hay una que se ejecuta cuando se registra un usuario.
  • Podía guardar esta información en la metadata del usuario en Auth0.
  • Podía registrar o actualizar el usuario en mi base de datos después del login.

En este caso escogí irme por la tercera, ya que iba a necesitar crear relaciones en mi base de datos, y me servía guardar toda la información justo después del login.

Así que cree mi aplicación en el dashboard de Auth0, lo configuré, instalé las dependencias, y todo funcionaba como decía en el quickstart, pero ahora necesitaba ir a ver la documentación de una de las dependencias para ajustar la configuración.

Auth0 tiene una librería que se llama ‘express-openid-connect’, ellos la mantienen y la documentación está bien en su mayoría. Así que busqué y vi que dentro de la configuración podía pasarle una función llamada afterCallback(), esta se ejecuta luego del login por lo que iba a funcionar, lo que pasa es que esta función no trae nada de la información del usuario que acaba de hacer login y no está disponible en las variables de sesión hasta este punto. De acuerdo a la documentación, podía obtener la información de usuario utilizando req.oidc.fetchUserInfo() pero esta en realidad no funcionaba. Luego como otra alternativa, en la documentación sugiere hacer un request al issuer para obtener la información, pero omitió el hecho de que hay que enviar headers de autorización en el request, lo que pasa es que no tenía de dónde sacarlos…

Así que luego de buscar en distintos blogs, foros, issues y stack overflow, al final vi que podía hacer un cambio en la configuración, para obtener los tokens para hacer el request manualmente. Mi configuración y mi función se ven así:

Snippet de código de muestra

Hasta este punto todo está funcionando, y voy a explicar después el funcionamiento de la base de datos, en el código anterior puede ver que estoy creando o actualizando el usuario, así que la próxima vez voy a explicar cómo lo hice.

Gracias por leer, espero que haya podido aprender algo o que se haya entretenido. Si tiene alguna sugerencia puede contactarme a mi instagram.