GraphQL - Cliente de autenticación

La autenticación es el proceso o acción de verificar la identidad de un usuario o un proceso. Es importante que una aplicación autentique a un usuario para garantizar que los datos no estén disponibles para un usuario anónimo. En esta sección, aprenderemos cómo autenticar un cliente GraphQL.

Express JWT

En este ejemplo, usaremos jQuery para crear una aplicación cliente. Para autenticar solicitudes, usaremos express-jwt módulo en el lado del servidor.

El módulo express-jwt es un middleware que le permite autenticar solicitudes HTTP utilizando tokens JWT. JSON Web Token (JWT) es una cadena larga que identifica al usuario que inició sesión.

Una vez que el usuario inicia sesión correctamente, el servidor genera un token JWT. Este token identifica claramente un registro. En otras palabras, el token es una representación de la identidad del usuario. Entonces, la próxima vez, cuando el cliente llegue al servidor, tendrá que presentar este token para obtener los recursos necesarios. El cliente puede ser una aplicación móvil o una aplicación web.

Ilustración

Seguiremos un procedimiento paso a paso para comprender esta ilustración.

Configurar el servidor

Los siguientes son los pasos para configurar el servidor:

Paso 1: descargue e instale las dependencias necesarias para el proyecto

Crea una carpeta auth-server-app. Cambie su directorio a auth-server-app desde la terminal. Siga los pasos 3 a 5 explicados en el capítulo Configuración del entorno.

Paso 2: crea un esquema

Añadir schema.graphql archivo en la carpeta del proyecto  auth-server-app y agregue el siguiente código -

type Query
{
   greetingWithAuth:String
}

Paso 3: agregar resolutores

Crea un archivo resolvers.js en la carpeta del proyecto y agregue el siguiente código:

El resolutor verificará si un objeto de usuario autenticado está disponible en el objeto de contexto de GraphQL. Generará una excepción si un usuario autenticado no está disponible.

const db = require('./db')

const Query = {
   greetingWithAuth:(root,args,context,info) => {

      //check if the context.user is null
      if (!context.user) {
         throw new Error('Unauthorized');
      }
      return "Hello from TutorialsPoint, welcome back : "+context.user.firstName;
   }
}

module.exports = {Query}

Paso 4: crear el archivo Server.js

El middleware de autenticación autentica a las personas que llaman mediante un token web JSON. La URL para la autenticación es http://localhost:9000/login.

Esta es una operación posterior. El usuario debe enviar su correo electrónico y contraseña que serán validados desde el backend. Si se genera un token válido utilizando el método jwt.sign, el cliente tendrá que enviarlo en el encabezado para solicitudes posteriores.

Si el token es válido, req.user se configurará con el objeto JSON decodificado para ser utilizado por el middleware posterior para la autorización y el control de acceso.

El siguiente código utiliza dos módulos: jsonwebtoken y express-jwt para autenticar solicitudes.

  • Cuando el usuario hace clic en el greet, se emite una solicitud para la ruta / graphql. Si el usuario no está autenticado, se le pedirá que se autentique.

  • Al usuario se le presenta un formulario que acepta la identificación de correo electrónico y la contraseña. En nuestro ejemplo, la ruta / login es responsable de autenticar al usuario.

  • La ruta / login verifica si se encuentra una coincidencia en la base de datos para las credenciales proporcionadas por el usuario.

  • Si las credenciales no son válidas, se devuelve al usuario una excepción HTTP 401.

  • Si las credenciales son válidas, el servidor genera un token. Este token se envía como parte de la respuesta al usuario. Esto lo hace la función jwt.sign.

const expressJwt = require('express-jwt');
const jwt = require('jsonwebtoken');

//private key
const jwtSecret = Buffer.from('Zn8Q5tyZ/G1MHltc4F/gTkVJMlrbKiZt', 'base64');

app.post('/login', (req, res) => {
   const {email, password} = req.body;
   
   //check database
   const user = db.students.list().find((user) =>  user.email === email);
   if (!(user && user.password === password)) {
      res.sendStatus(401);
      return;
   }
   
   //generate a token based on private key, token doesn't have an expiry
   const token = jwt.sign({sub: user.id}, jwtSecret);
   res.send({token});
});

Para cada solicitud, se llamará a la función app.use (). Esto, a su vez, invocará el middleware expressJWT. Este middleware decodificará el JSON Web Token. La identificación de usuario almacenada en el token se recuperará y almacenará como un usuario de propiedad en el objeto de solicitud.

//decodes the JWT and stores in request object
app.use(expressJwt({
   secret: jwtSecret,
   credentialsRequired: false
}));

Para que la propiedad del usuario esté disponible dentro del contexto GraphQL, esta propiedad se asigna al context objeto como se muestra a continuación -

//Make req.user available to GraphQL context
app.use('/graphql', graphqlExpress((req) => ({
   schema,
   context: {user: req.user &&apm; db.students.get(req.user.sub)}
})));

Crear server.js en la ruta de la carpeta actual. El archivo server.js completo es el siguiente:

const bodyParser = require('body-parser');
const cors = require('cors');
const express = require('express');
const expressJwt = require('express-jwt'); //auth
const jwt = require('jsonwebtoken'); //auth
const db = require('./db');

var port = process.env.PORT || 9000
const jwtSecret = Buffer.from('Zn8Q5tyZ/G1MHltc4F/gTkVJMlrbKiZt', 'base64');
const app = express();

const fs = require('fs')
const typeDefs = fs.readFileSync('./schema.graphql',{encoding:'utf-8'})
const resolvers = require('./resolvers')
const {makeExecutableSchema} = require('graphql-tools')

const schema = makeExecutableSchema({typeDefs, resolvers})

app.use(cors(), bodyParser.json(), expressJwt({
   secret: jwtSecret,
   credentialsRequired: false
}));

const  {graphiqlExpress,graphqlExpress} = require('apollo-server-express')

app.use('/graphql', graphqlExpress((req) => ({
   schema,
   context: {user: req.user && db.students.get(req.user.sub)}
})));
app.use('/graphiql',graphiqlExpress({endpointURL:'/graphql'}))

//authenticate students
app.post('/login', (req, res) => {
   const email = req.body.email;
   const password = req.body.password;

   const user = db.students.list().find((user) =>  user.email === email);
   if (!(user && user.password === password)) {
      res.sendStatus(401);
      return;
   }
   const token = jwt.sign({sub: user.id}, jwtSecret);
   res.send({token});
});

app.listen(port, () => console.info(`Server started on port ${port}`));

Paso 5: ejecutar la aplicación

Ejecute el comando  npm start en la terminal. El servidor estará funcionando en el puerto 9000. Aquí, usamos GraphiQL como cliente para probar la aplicación.

Abra el navegador y escriba la URL http://localhost:9000/graphiql. Escriba la siguiente consulta en el editor:

{
   greetingWithAuth
}

En la siguiente respuesta, obtuvimos un error porque no somos usuarios autenticados.

{
   "data": {
      "greetingWithAuth": null
   },
   "errors": [
      {
         "message": "Unauthorized",
         "locations": [
            {
               "line": 2,
               "column": 3
            }
         ],
         "path": [
            "greetingWithAuth"
         ]
      }
   ]
}

En la siguiente sección, creemos una aplicación cliente para autenticar.

Configuración del cliente JQuery

En la aplicación cliente, se proporciona un botón de saludo que invocará el esquema greetingWithAuth. Si hace clic en el botón sin iniciar sesión, aparecerá el siguiente mensaje de error:

Una vez que inicie sesión con un usuario disponible en la base de datos, aparecerá la siguiente pantalla:

Acceder greeting, primero debemos acceder a la URL http://localhost:9000/login ruta como se muestra a continuación.

La respuesta contendrá el token generado desde el servidor.

$.ajax({
   url:"http://localhost:9000/login",
   contentType:"application/json",
   type:"POST",
   data:JSON.stringify({email,password}),
   success:function(response) {
      loginToken = response.token;
      $('#authStatus')
      .html("authenticated successfully")
      .css({"color":"green",'font-weight':'bold'});
      $("#greetingDiv").html('').css({'color':''});
   },
   error:(xhr,err) =>  alert('error')
})

Después de un inicio de sesión exitoso, podemos acceder al esquema greetingWithAuth como se indica a continuación. Debe haber un Authorizationheader para todas las solicitudes posteriores con token de portador.

{ 
   url: "http://localhost:9000/graphql",
   contentType: "application/json",
   headers: {"Authorization": 'bearer '+loginToken},  type:'POST',
   data: JSON.stringify({
   query:`{greetingWithAuth}`
}

El siguiente es el código para index.html:

<!DOCTYPE html>
<html>
   <head>
      <script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      <script>
         $(document).ready(function() {
            let loginToken = "";
            $("#btnGreet").click(function() {
                  $.ajax({url: "http://localhost:9000/graphql",
                  contentType: "application/json",
                  headers: {"Authorization": 'bearer '+loginToken},
                  type:'POST',
                  data: JSON.stringify({
                  query:`{greetingWithAuth}` }),
                  success: function(result) {
                  $("#greetingDiv").html("<h1>"+result.data.greetingWithAuth+"</h1>")
                  },
                  error:function(jQxhr,error) {
                     if(jQxhr.status == 401) {
                        $("#greetingDiv").html('please authenticate first!!')
                        .css({"color":"red",'font-weight':'bold'})
                        return;
                     }
                     $("#greetingDiv").html('error').css("color","red");
                  }
               });
            });
            $('#btnAuthenticate').click(function() {
               var email =  $("#txtEmail").val();
               var password =  $("#txtPwd").val();
               if(email && password) {
                  $.ajax({
                     url:"http://localhost:9000/login",
                     contentType:"application/json",
                     type:"POST",
                     data:JSON.stringify({email,password}),
                     success:function(response) {
                        loginToken =  response.token;
                        $('#authStatus')
                        .html("authenticated successfully")
                        .css({"color":"green",'font-weight':'bold'});
                        $("#greetingDiv").html('').css({'color':''});
                     },
                     error:(xhr,err) =>  alert('error')
                  })
               }else alert("email and pwd empty")
            })
         });
      </script>
   </head>
   
   <body>
      <h1> GraphQL Authentication </h1>
      <hr/>
      <section>
         <button id = "btnGreet">Greet</button>
         <br/> <br/>
         <div id = "greetingDiv"></div>
      </section>
      <br/> <br/> <br/>
      <hr/>
      
      <section id = "LoginSection">
         <header>
            <h2>*Login first to  access greeting </h2>
         </header>
         <input type = "text" value = "[email protected]" placeholder = "enter email" id = "txtEmail"/>
         <br/>
         
         <input type = "password" value = "pass123" placeholder = "enter password" id = "txtPwd"/>
         <br/>
         
         <input type = "button" id = "btnAuthenticate"  value = "Login"/>
         <p id = "authStatus"></p>
      </section>
   </body>
</html>