GraphQL - Autenticazione del client

L'autenticazione è il processo o l'azione di verifica dell'identità di un utente o di un processo. È importante che un'applicazione autentichi un utente per garantire che i dati non siano disponibili per un utente anonimo. In questa sezione, impareremo come autenticare un client GraphQL.

Express JWT

In questo esempio, useremo jQuery per creare un'applicazione client. Per autenticare le richieste, useremo express-jwt modulo sul lato server.

Il modulo express-jwt è un middleware che consente di autenticare le richieste HTTP utilizzando i token JWT. JSON Web Token (JWT) è una lunga stringa che identifica l'utente connesso.

Una volta che l'utente accede correttamente, il server genera un token JWT. Questo token identifica distintamente un registro. In altre parole, il token è una rappresentazione dell'identità dell'utente. Quindi la prossima volta, quando il client arriva al server, deve presentare questo token per ottenere le risorse richieste. Il client può essere un'applicazione mobile o un'applicazione web.

Illustrazione

Seguiremo una procedura graduale per comprendere questa illustrazione.

Configurazione del server

Di seguito sono riportati i passaggi per configurare il server:

Passaggio 1: scaricare e installare le dipendenze richieste per il progetto

Crea una cartella auth-server-app. Cambia la tua directory in auth-server-app dal terminale. Seguire i passaggi da 3 a 5 spiegati nel capitolo Configurazione dell'ambiente.

Passaggio 2: creare uno schema

Inserisci schema.graphql file nella cartella del progetto auth-server-app e aggiungi il seguente codice -

type Query
{
   greetingWithAuth:String
}

Passaggio 3: aggiungere i resolver

Crea un file resolvers.js nella cartella del progetto e aggiungi il seguente codice -

Il risolutore verificherà se un oggetto utente autenticato è disponibile nell'oggetto contesto di GraphQL. Solleverà un'eccezione se un utente autenticato non è disponibile.

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}

Passaggio 4: creare il file Server.js

Il middleware di autenticazione autentica i chiamanti utilizzando un token Web JSON. L'URL per l'autenticazione è http://localhost:9000/login.

Questa è un'operazione post. L'utente deve inviare la sua email e password che verranno convalidate dal backend. Se viene generato un token valido utilizzando il metodo jwt.sign, il client dovrà inviarlo nell'intestazione per le richieste successive.

Se il token è valido, req.user verrà impostato con l'oggetto JSON decodificato per essere utilizzato dal middleware successivo per l'autorizzazione e il controllo dell'accesso.

Il codice seguente utilizza due moduli: jsonwebtoken ed express-jwt per autenticare le richieste -

  • Quando l'utente fa clic sul file greetpulsante, viene emessa una richiesta per il percorso / graphql. Se l'utente non è autenticato, gli verrà chiesto di autenticarsi.

  • All'utente viene presentato un modulo che accetta ID e-mail e password. Nel nostro esempio, la rotta / login è responsabile dell'autenticazione dell'utente.

  • La route / login verifica se nel database viene trovata una corrispondenza per le credenziali fornite dall'utente.

  • Se le credenziali non sono valide, all'utente viene restituita un'eccezione HTTP 401.

  • Se le credenziali sono valide, il server genera un token. Questo token viene inviato come parte della risposta all'utente. Questo viene fatto dalla funzione 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});
});

Per ogni richiesta verrà chiamata la funzione app.use (). Questo a sua volta richiamerà il middleware expressJWT. Questo middleware decodificherà il token Web JSON. L'ID utente memorizzato nel token verrà recuperato e archiviato come utente di proprietà nell'oggetto richiesta.

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

Per rendere disponibile la proprietà utente nel contesto GraphQL, questa proprietà viene assegnata al file context oggetto come mostrato di seguito -

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

Creare server.js nel percorso della cartella corrente. Il file server.js completo è il seguente:

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}`));

Passaggio 5: eseguire l'applicazione

Esegui il comando  npm start nel terminale. Il server sarà attivo e funzionante sulla porta 9000. Qui, usiamo GraphiQL come client per testare l'applicazione.

Apri il browser e digita l'URL http://localhost:9000/graphiql. Digita la seguente query nell'editor:

{
   greetingWithAuth
}

Nella risposta seguente, abbiamo ricevuto un errore poiché non siamo utenti autenticati.

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

Nella sezione successiva, creiamo un'applicazione client per l'autenticazione.

Configurazione del client JQuery

Nell'applicazione client, viene fornito un pulsante di saluto che richiamerà lo schema greetingWithAuth. Se fai clic sul pulsante senza accedere, ti verrà visualizzato il messaggio di errore come di seguito:

Una volta effettuato l'accesso con un utente disponibile nel database, apparirà la seguente schermata:

Accedere greeting, dobbiamo prima accedere all'URL http://localhost:9000/login percorso come di seguito.

La risposta conterrà il token generato dal server.

$.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')
})

Dopo un login riuscito, possiamo accedere allo schema greetingWithAuth come indicato di seguito. Dovrebbe essere presente un'intestazione di autorizzazione per tutte le richieste successive con token di connessione.

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

Quello che segue è il codice per 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>