GraphQL — Autorización de usuarios mediante JSON Web Tokens en express-graphql

Cómo obtener el token del usuario proporcionado en la cabecera HTTP Authorization

Dailos Rafael Díaz Lara
CanariasJS

--

Una vez alcanzado cierto nivel en el desarrollo de nuestra API basada en GraphQL, llega un momento en el que nos empiezan a surgir determinadas dudas acerca de cómo gestionar los procesos de autorización para las queries y las mutations.

Una de las metodologías más extendidas para hacer esto, es usar el sistema JSON Web Token (JWT) de identificación de usuario. No obstante, a pesar de que la documentación oficial de GraphQL, en lo relacionado con los procesos de autorización, nos dice que estos son relativamente fáciles de implementar, desde mi punto de vista, la documentación es un poco reducida y tiene una seria carencia de ejemplos prácticos.

Con todo esto, a lo largo del presente texto voy a exponer cómo obtengo los tokens JWT de usuarios para cada petición (query o mutation) en aquellos proyectos donde uso una API basada en express-graphql.

Proyecto base

El código completo empleado para redactar este documento, puede encontrarse haciendo clic en este enlace.

¿Qué contiene este código? Pues bien, este código implementa un servidor Express (v4.15) con el middleware express-graphql (v0.6.7), funcionando sobre un proceso NodeJS (v8.4).

Debido a que el objetivo de este documento es mostrar cómo obtener la cabecera HTTP Authorization, he escrito una query y una mutation realmente básicas.

Además de esto, el código del servidor es el siguiente:

De este modo, cuando arrancamos el servidor (npm start), si abrimos un navegador web y accedemos a la interfaz de GraphiQL, obtendremos el siguiente resultado:

Llegados a este punto, podemos confirmar que nuestra API basada en GraphQL esta funcionando correctamente.

Punto de partida

Ahora que ya tenemos el código básico funcionando, me gustaría aclarar un concepto.

Mi idea es la de tener accesible el token del usuario en cada función resolve. De este modo, antes de ejecutar el código específico en cada momento, seré capaz de verificar la validez del token y, dependiendo del resultado de dicha comprobación, ejecutaré la operación correspondiente o emitiré un error específico.

Partiendo de este punto, la documentación oficial nos dice que la función resolve puede recibir, al menos, tres parámetros de entrada: parentValues, args y context. Éste último será el objetivo a abordar en el presente documento.

Una vez aclarado este punto, vamos a analizar las dos opciones que express-graphql nos proporciona para obtener la cabecera HTTP Authorization: valor de context por defecto y valor de context definido por nosotros.

Primera aproximación — No definir el objeto context

En el repositorio de express-graphql en GitHub podemos encontrar la siguiente información:

context: A value to pass as the context to the graphql() function from GraphQL.js/src/execute.js. If contextis not provided, the request object is passed as the context.

La parte más importante de la definición, para este documento, es la última frase ya que, en el objeto request será donde se encontrará la cabecera que estamos buscando.

Si recordamos el código mostrado al principio, en la declaración del endpoint de /graphql, no definimos ningún valor para el parámetro context.

Con esta definición de endpoint y basándonos en la documentación oficial de express-graphql, voy a modificar el método resolve de la query de pruebas para que quede de la siguiente manera:

Como se puede apreciar, he introducido tres líneas de traza para obtener las claves del objeto context, las claves del objeto context.header y además, el valor de la cabecera Authorization.

En este punto, la interfaz GraphiQL ya no nos es de utilidad por que no nos permite definir cabeceras HTTP. Debido a esto, voy a usar Postman con los siguientes parámetros:

En la primera imagen he usado el Content-Type para definir el formato de dato en el que se van a enviar la petición y además, he definido la cabecera Authorization a la cual, le he asignado un token de prueba, basado en JWT.

Por otro lado, en la segunda imagen se muestra la petición a GraphQL formateada en un objeto JSON.

De este modo, si ejecutamos la petición, recibiremos el siguiente resultado en Postman:

Como podemos ver, hemos recibido la misma respuesta que recibimos en GraphiQL, por parte la API.

La parte más importante no es Postman sino lo que se muestra en la consola donde se está ejecutando la API.

Perfecto!!! Hemos obtenido el token JWT del usuario que fue enviado vía HTTP desde Postman. Con este código dentro de nuestras funciones resolve, ahora seremos capaces de implementar cualquier regla de comprobación de autorización y validez que queramos.

Error por sobrescritura del objeto context

Si continuamos con la primera aproximación que hemos analizado, corremos el riesgo de que, en el caso de que nuestra aplicación continúe creciendo, tengamos que definir o proporcionar un objeto context personalizado en el endpoint /graphql, como por ejemplo:

Basándonos en esto, si reenviamos la petición a través de Postman, el resultado de la consulta será el mismo pero en la consola, no seremos capaces de obtener la cabecera Authorization.

Como resultado de lo indicado anteriormente, el objeto context ha sido sobrescrito en la definición del endpoint y por lo tanto, la información de la petición HTTP ya no está disponible.

Segunda aproximación — Inyectar el objeto request en el objeto context

Para evitar la sobrescritura del objeto context, la documentación de express-graphql nos proporciona una definición más útil del método graphqlHTTP.

Con esta aproximación vamos a inyectar una función anónima en el método graphqlHTTP.

Esta función anónima recibirá tres atributos entre los cuales, uno de ellos es el objeto request.

De esta manera, para obtener la cabecera Authorization en cada función resolve, tenemos que definir el objeto context, proporcionando el objeto request como un atributo más.

Obviamente, para acceder a este nuevo atributo será necesario editar la forma en la que obtenemos la información.

Con esta pequeña modificación, si reenviamos la petición con Postman, en nuestra consola seremos capaces de recibir la siguiente información:

De ahora en adelante, ya no nos preocuparemos de si tenemos que expandir el objeto context de nuestra API GraphQL porque, mientras continuemos proporcionando el objeto request como atributo de context, seremos capaces de acceder a la cabecera Authorization.

Detalles finales

Todos los procesos abordados hasta ahora se han centrado en las operaciones de query. Sin embargo, debido a que tanto la función resolve como el objeto context son comunes para ambos, tanto queries como mutations, seremos capaces de acceder a los token de los usuarios en las mutations, de igual manera que se ha expuesto para las queries.

Conclusiones

Para obtener el token de usuario proporcionado por una aplicación cliente, tenemos que aprovecharnos de la propiedad context proporcionada por GraphQL así como de la inyección dentro de éste, del objeto request proporcionado por Express.

Así, la información almacenada en request estará accesible para cualquier función resolve de nuestra API GraphQL.

Espero que este documento te haya sido útil. Si es así, por favor, dale un voto positivo y compártelo en tus redes sociales.

Gracias y saludos.

--

--

Dailos Rafael Díaz Lara
CanariasJS

Multiplatform Software Developer seeking for new challenges