Node.js

Autenticación JWT con GraphQL, Node.js y Couchbase NoSQL

Hace unos meses, cuando empecé a aprender sobre GraphQL, había escrito un tutorial anterior para usarlo con Couchbase y Node.js. El tutorial se centró en los conceptos básicos que incluían la creación de objetos GraphQL y la consulta de esos objetos desde la base de datos NoSQL, Couchbase. Avancemos un poco y escribí un tutorial que ofrecía un forma alternativa para utilizar GraphQL con Node.js, aunque la capa de base de datos no era el énfasis.

Al utilizar o crear una API, a menudo se dan situaciones en las que no se desea que todos los datos sean accesibles para todo el mundo. En estos escenarios, querrás algún tipo de regulación a través de autorización y tokens API. Al igual que con una API RESTful, esto se puede lograr fácilmente a través de tokens web JSON (JWT).

Vamos a ver cómo utilizar JWT en un archivo GraphQL aplicación para proteger determinados datos en lugar de todos o ninguno.

De cara al futuro, probablemente sea una buena idea entender al menos un poco cómo funcionan los tokens web JSON (JWT). Si quieres echar un vistazo a una guía rápida para empezar, echa un vistazo a mi tutorial titulado, Autenticación JWT en una API basada en Node.js. El enfoque de ese tutorial no es GraphQL, pero sigue funcionando como un buen punto de partida.

El objetivo de este tutorial es crear una API que utilice GraphQL. Podremos crear cuentas, obtener tokens, y usar esos tokens para acceder a partes protegidas de nuestra API. Las instrucciones para instalar y configurar Couchbase no serán parte de este tutorial, pero no es necesario hacer nada especial por compatibilidad.

Creación de una nueva aplicación Node.js con las dependencias del proyecto

Antes de saltar a las partes más pesadas y complejas de nuestro proyecto, vamos a crear un proyecto fresco con todas las dependencias y algo de código boilerplate. Asumiendo que ya tienes Node.js instalado y configurado, ejecuta lo siguiente desde tu CLI:

Los comandos anteriores crearán un nuevo paquete.json e instalar cada uno de nuestros paquetes necesarios. Estos paquetes incluyen una librería GraphQL así como una extensión GraphQL para Express Framework. También incluimos una forma de hacer hash de nuestros datos de contraseña, crear JWT, y almacenar todo en Couchbase. ¿Podría haber hecho esto con una sola línea? Sí, pero pensé que sería más fácil de leer si lo dividía.

Con el proyecto creado, sigue adelante y crea un app.js que incluye el siguiente código:

Vamos a llamar al código anterior nuestro código boilerplate. Esencialmente estamos haciendo algunas configuraciones y nada realmente relevante para nuestro objetivo final. Estamos importando nuestros paquetes descargados, estableciendo una conexión con Couchbase, configurando Express Framework, definiendo tres endpoints, y sirviendo en el puerto 3000.

Asegúrate de usar tu propia información de Couchbase en lugar de la información que yo usé. Además, para mayor seguridad, cambia la directiva jwt-secret por lo que sus fichas son hash de una manera menos predecible que la mía.

Diseño de puntos finales de la API de cuentas simples para el inicio de sesión y el registro

Usted notará en nuestro código que tenemos un punto final tanto para el registro como para el inicio de sesión. Hay muchas maneras de lograr lo que estamos tratando de hacer, probablemente algunas mejores que mi solución. Sin embargo, encontré lo que vamos a ver lógico y fácil de implementar sin alejarnos demasiado de nuestros objetivos.

Empezando por el /registrarse punto final, tenemos lo siguiente:

Cuando un usuario envía una solicitud POST con nombre de usuario y contraseña en el cuerpo, primero estamos creando un nuevo UUID para ser utilizado como nuestra clave de documento Couchbase, entonces estamos hash de la contraseña utilizando Bcrypt, como demostrado anteriormente en un tutorial que escribí.

Una vez creado el hash, almacenamos los datos del perfil en Couchbase y los devolvemos. El objetivo de /registrarse es darnos algunos datos con los que autenticarnos. Para autenticarnos, utilizamos el método /inicio de sesión como se ve a continuación:

En el código anterior, estamos creando una consulta N1QL para encontrar un documento que contenga un nombre de usuario que coincida con el nombre de usuario que se pasó con la solicitud. Estamos utilizando una consulta parametrizada para evitar cualquier tipo de ataque de inyección SQL.

Si se encuentra un documento, comparamos la contraseña almacenada con la contraseña que se pasó con la solicitud. Si coincide, podemos firmar el ID de usuario utilizando nuestro secreto, lo que nos da un JWT que se utilizará en futuras solicitudes.

Algunas cosas a tener en cuenta con nuestros dos puntos finales:

  1. No estamos haciendo ninguna validación de datos. Este es un ejemplo y estamos tratando de mantener las cosas simples.
  2. No estamos estableciendo una caducidad en nuestro JWT. Lo normal es que caduque en una hora, pero esto es solo un ejemplo sencillo.

Por ahora, no hemos hecho nada con GraphQL. Sólo hemos sentado las bases para crear usuarios y obtener tokens web JSON. Aunque estamos creando JWT, por ahora no estamos validando que sean correctos.

Validación de tokens web JSON (JWT) con una función de Express Framework

Cuando se trata de trabajar con datos protegidos, tener un JWT no es suficiente. Queremos asegurarnos de que el token es válido comprobando la firma. También necesitamos una solución para pasar JWT alrededor.

Express Framework tiene middleware para trabajar con tokens web JSON, pero encontré que la documentación era escasa. En su lugar, me pareció más fácil crear mi propio método de validación. Tome lo siguiente:

¿Qué hacemos en el código anterior?

Dado que estamos utilizando uso.appen cada petición, esta función será llamada. Cuando se llama a esta función, miramos las cabeceras de la petición actual y buscamos una cabecera de autorización. Si existe una cabecera de autorización, nos aseguramos de que es un token portador y usamos el valor del token real en nuestra verificación. El token real es nuestro JWT. Si el token es válido, podemos añadir el valor decodificado a la solicitud, que será accesible en la siguiente fase de nuestra solicitud. Si no hay token presente, no te preocupes porque no pasará nada. Nuestra lógica sólo comprueba nuestros tokens si existen porque no todos los puntos de datos estarán protegidos.

¿Hay mejores formas de obtener nuestro token JWT en una petición y validarlo? Probablemente, pero el código anterior funcionó cuando lo probé y no era demasiado complicado.

Desarrollo de una API parcialmente protegida con GraphQL y JavaScript

Ahora que tenemos la lógica JWT en su lugar, podemos centrarnos en el desarrollo de nuestra API. Técnicamente la /inicio de sesión y /registrarse forman parte de nuestra API, pero se centran en GraphQL.

Antes de preocuparnos por las consultas, centrémonos en nuestros objetos GraphQL:

Recuerda, si viste mi anterior Tutorial de Couchbase con GraphQLprobablemente los objetos de arriba parezcan un poco diferentes. Esto se debe a que los modelé después de mi tutorial alternativo de GraphQL. Esencialmente, tenemos dos objetos donde uno es para los datos de la cuenta y el otro es para los datos del curso. Tal vez no sea el mejor ejemplo, pero haremos que funcione.

Los datos del curso harán referencia a una cuenta para el autor del curso. Crearemos un método de resolución para la información del autor, pero todavía no. Primero vamos a crear una consulta para los datos de la cuenta por separado:

Es lógico que cuando tenemos una consulta como cuentaQueremos obtener datos de una cuenta en particular y probablemente sea nuestra propia cuenta. Si estamos consultando nuestra propia cuenta, probablemente deberíamos tener un JWT válido.

Fíjate en que estamos utilizando context.decodeToken en el código anterior. La dirección contexto nos permite obtener datos de la petición y, en nuestro caso, los datos decodificados del token podrían existir en la petición. Si no existe, probablemente deberíamos lanzar un error porque un token válido es un requisito para esta consulta.

Si tenemos un token válido, podemos crear una consulta N1QL y utilizar el id de usuario para consultar nuestra cuenta y devolverla.

No está mal, ¿verdad?

Ampliemos nuestras consultas. Dentro de la campos vamos a añadir otra consulta, pero esta vez vamos a consultar los datos del curso y los datos del curso no estarán necesariamente protegidos.

En el código anterior, estamos haciendo una simple consulta N1QL para todos los cursos almacenados en la base de datos. El problema aquí es que para los datos de autor, sólo estamos obteniendo la clave, no los datos de cuenta completamente cargados que están protegidos.

En realidad vamos a hacer algunas manipulaciones de datos en el objeto GraphQL para Tipo de curso en su lugar:

Observe que ahora tenemos un resolver en el autor propiedad. En esta función, comprobamos si hay un token válido y, si no lo encontramos, devolvemos un error. Esta comprobación y el error sólo se producen si la propiedad autor se solicitan datos. Si la consulta no solicita autor podemos obtener el resto de la información sin un token, ya que no está protegida.

Aquí hay otro problema. No sólo estamos comprobando para asegurarnos de que un JWT válido está presente, sino que también queremos que el JWT coincida con los datos devueltos en la consulta N1QL. Esto significa que si tenemos varios autores, sólo los autores para los que tenemos permiso tendrán éxito. De nuevo, no es el mejor ejemplo, pero demuestra nuestro punto.

Para acabar con los cabos sueltos, tenemos que actualizar nuestro /graphql punto final:

Todo lo que hemos hecho es añadir nuestro esquema que incluye nuestras dos posibles consultas.

Conclusión

Aunque nuestro ejemplo era simple, nos permitió demostrar consultas protegidas y piezas de datos con GraphQL, Couchbasey tokens web JSON (JWT). Algunas cosas básicas para recordar:

  1. Se puede acceder a los datos solicitados en GraphQL contexto variable.
  2. Los tokens web JSON y las cuentas probablemente deberían crearse a través de endpoints separados en lugar de con mutaciones GraphQL.
  3. Con JWT se pueden restringir tanto las consultas como las propiedades de los datos. No se trata de una serie de eventos de todo o nada.

Si quieres aprender más sobre GraphQL, te sugiero que eches un vistazo a mi tutorial anterior sobre el tema. Para saber más sobre Couchbase con Node.js, echa un vistazo al Portal para desarrolladores de Couchbase.

Comparte este artículo
Recibe actualizaciones del blog de Couchbase en tu bandeja de entrada
Este campo es obligatorio.

Autor

Publicado por Nic Raboy, Defensor del Desarrollador, Couchbase

Nic Raboy es un defensor de las tecnologías modernas de desarrollo web y móvil. Tiene experiencia en Java, JavaScript, Golang y una variedad de frameworks como Angular, NativeScript y Apache Cordova. Nic escribe sobre sus experiencias de desarrollo relacionadas con hacer el desarrollo web y móvil más fácil de entender.

Deja un comentario

¿Listo para empezar con Couchbase Capella?

Empezar a construir

Consulte nuestro portal para desarrolladores para explorar NoSQL, buscar recursos y empezar con tutoriales.

Utilizar Capella gratis

Ponte manos a la obra con Couchbase en unos pocos clics. Capella DBaaS es la forma más fácil y rápida de empezar.

Póngase en contacto

¿Quieres saber más sobre las ofertas de Couchbase? Permítanos ayudarle.