‫Jonas: Quindi
‫in questa lezione implementeremo la funzionalità
‫di accesso degli utenti in base a una determinata
‫password e indirizzo e-mail.
‫E proprio come prima, il concetto di accesso di
‫un utente significa sostanzialmente firmare un token Web
‫JSON e inviarlo al client.
‫Ma in questo caso emettiamo il token solo
‫nel caso in cui l'utente esista effettivamente
‫e che la password sia corretta.
‫Quindi, iniziamo a implementarlo.
‫Esportazioni. l'accesso è...
‫richiesta, risposta e successivo.
‫E la prima cosa è leggere
‫effettivamente l'e-mail e la password dal corpo.
‫Quindi diciamo che const email è uguale a req. corpo. e-mail.
‫Ok, e ora eslint ci dà questo avviso, o
‫in realtà questo errore, e
‫dice che dovremmo usare la destrutturazione degli oggetti, ok?
‫E quindi ho lasciato che accadesse
‫qui apposta solo per mostrarti come possiamo farlo
‫con la destrutturazione di ES6.
‫E penso di averne parlato prima nel corso, e spero
‫anche che tu abbia già familiarità
‫con il concetto, ma comunque, dal
‫momento che il nome
‫di questa proprietà qui è lo stesso
‫del nome della variabile, possiamo semplicemente
‫farlo come questo e sbarazzati del nome della proprietà qui, ok?
‫E vogliamo anche ottenere la password.
‫E quindi sarebbe lo stesso, quindi in
‫pratica la password è uguale a una richiesta. corpo. parola d'ordine.
‫E invece
‫di farlo, possiamo semplicemente inserire email e password.
‫E quindi proprio così,
‫creiamo fondamentalmente queste due variabili dell'oggetto corpo, ok?
‫Quindi fondamentalmente questo è il
‫modo in cui l'utente invierà le credenziali di accesso per consentirci di verificare.
‫Ok, e quel processo di controllo ha un paio
‫di passaggi, quindi inseriamoli
‫qui prima di iniziare a codificare.
‫Quindi prima dobbiamo verificare se
‫email e password esistono effettivamente, ok?
‫Quindi numero due, controlla se l'utente esiste e
‫allo stesso tempo se la password è corretta.
‫E poi terzo, se
‫tutto va bene, invia il token, quindi il token Web JSON al client.
‫Ok, quindi il primo è molto facile.
‫Quindi, se non ci sono e-mail o password, vogliamo inviare
‫un messaggio di errore al nostro cliente.
‫Ora come lo faremo?
‫Bene, lo faremo usando
‫gli strumenti che abbiamo implementato proprio nell'ultima sezione.
‫Quindi fondamentalmente il nostro errore dell'app, lo ricordi?
‫Quindi creeremo semplicemente un nuovo errore
‫qui e il nostro middleware
‫di gestione degli errori globale lo raccoglierà e invierà l'errore al client.
‫E quindi iniziamo effettivamente importando quell'errore.
‫Quindi ricorda che è
‫questo qui dalla nostra classe AppError, giusto?
‫Quindi diciamo AppError e poi require.
‫Quindi un livello più alto da qui, poi nelle utility e
‫poi appError.
‫Ok, quindi dobbiamo chiamare il prossimo
‫middleware, ed è qui che passiamo l'errore, ricordi?
‫Quindi nuovo AppError e
‫il messaggio è: fornire e-mail e password.
‫E il codice di errore HTTP è 400 per una richiesta errata, ok?
‫E ora ho alcuni
‫errori qui, e questo mi sta aiutando di nuovo a correggere alcuni bug.
‫Quindi vedo che
‫quassù, di nuovo, l'ho usato con una T invece
‫di D, e se lo salvo ora, è tutto corretto.
‫Ok, quindi iniziamo a testarlo subito.
‫E per
‫questo inizierò semplicemente creando un token falso qui per ora, ok.
‫E allora diciamo res. stato 200 per ok.
‫JSON, e poi ovviamente, come
‫sempre, lo stato impostato su success, e poi anche il nostro token.
‫E questo è tutto ciò
‫che invieremo come risposta al login, ok?
‫Quindi nessun oggetto utente perché non è affatto necessario.
‫Tutto ciò che vogliamo come risposta per l'accesso è
‫in realtà il token.
‫Questo è tutto ciò che conta quando l'utente accede.
‫Successivamente, dobbiamo implementare il
‫percorso, quindi ripetiamolo nel nostro router utente, ok?
‫E questo è in realtà abbastanza simile a questo.
‫Duplichiamolo e poi sostituiamo la registrazione
‫in entrambi i casi qui con il login, ok?
‫E ancora, questo è valido solo per una richiesta
‫di posta, perché ovviamente vogliamo
‫inviare le credenziali di accesso nel corpo.
‫E così di nuovo
‫è un post, ma non un get, non una patch e
‫non un'eliminazione, perché in questo caso non ha alcun senso.
‫Allora proviamolo subito.
‫Copio anche questo URL qui.
‫Sarà posta, login e ovviamente il
‫corpo.
‫Quindi di nuovo, raw e JSON.
‫Quindi creiamo un corpo semplice qui.
‫Va bene, quindi proviamo a
‫inviarlo senza alcuna password e vediamo cosa succede.
‫E infatti funziona, quindi riceviamo il nostro messaggio qui:
‫per favore fornisci email e password.
‫E ancora, dal momento che siamo
‫in fase di sviluppo, stiamo ottenendo tutto questo
‫stack di errori qui e l'errore completo con il nostro
‫codice di stato, lo stato e ovviamente anche dicendo
‫che si tratta di un errore operativo.
‫Quindi, proprio come abbiamo definito nella sezione precedente, ok?
‫Ora proviamo lo stesso con la password.
‫Quindi password, impostala su Penso che fosse quella giusta,
‫quindi elimina l'e-mail.
‫E questo ovviamente innesca lo stesso.
‫Ok, e con tutto ciò che è corretto dovrebbe funzionare,
‫e in effetti qui otteniamo il nostro token falso.
‫Ora solo una cosa che dovremmo fare
‫qui è anche tornare.
‫Quindi l'abbiamo fatto molte volte prima e lascia
‫che te lo spieghi di nuovo.
‫È semplicemente perché dopo aver chiamato il
‫prossimo middleware, vogliamo assicurarci che questa
‫funzione di accesso qui finisca subito, va bene?
‫E poiché non lo avevamo
‫prima, ora vedi di nuovo questo errore qui.
‫Quindi questo è già abbastanza familiare a questo punto, giusto?
‫Quindi non è possibile impostare le intestazioni dopo che sono state inviate
‫al client, perché di nuovo, abbiamo inviato due risposte.
‫Prima la risposta all'errore, e poi anche questo codice qui eseguito, cosa che
‫ovviamente non avrebbe dovuto fare.
‫E così ancora una volta usiamo return qui per risolvere il problema.
‫Ok, ora controlliamo se esiste effettivamente un utente per
‫l'e-mail che è stata pubblicata.
‫Quindi const user, quindi ora usiamo il findOne in realtà, perché
‫questa volta non stiamo selezionando un utente dall'ID,
‫ma invece dalla sua email, giusto?
‫E quindi dobbiamo passare quell'oggetto
‫filtro in cui possiamo dire e-mail uguale a e-mail, ok?
‫Quindi il campo si chiama
‫e-mail e la variabile si chiama anche e-mail.
‫E così in ES6 possiamo abbreviarlo semplicemente come questo.
‫Ora, prima di andare
‫avanti qui, c'è davvero qualcosa
‫di importante che devo fare per la sicurezza.
‫Quindi lascia che te lo mostri
‫a Postman, dove abbiamo registrato un nuovo account.
‫E quindi vedi qui che
‫nell'output dell'utente, otteniamo effettivamente la password, ok?
‫In realtà è crittografato,
‫ma comunque non è una buona pratica divulgare
‫i dati della password al client, ok?
‫Ad esempio, se avessimo il nostro ottieni tutti gli
‫utenti qui e questo percorso non è
‫ancora implementato, ma se dovessimo ottenere tutti gli utenti dalla
‫raccolta, allora tutti avrebbero la
‫password visibile e non lo vogliamo , va bene?
‫E quindi aggiustarlo è in realtà abbastanza facile,
‫perché l'abbiamo fatto prima.
‫Tutto quello che dobbiamo
‫fare è dire la password, quindi selezionarla e impostarla su false.
‫E così in questo modo non verrà mai visualizzato automaticamente
‫in nessun output.
‫E lascia che
‫te lo dimostri in realtà iscrivendoti come qualcos'altro, così così.
‫E vediamo, oh, in realtà abbiamo
‫già un utente con quell'e-mail, quindi facciamo solo test3.
‫E ora la password in realtà è ancora qui, ma
‫immagino che sia semplicemente perché potremmo semplicemente crearla, questo
‫nuovo documento qui,
‫e quindi quindi non lo seleziona
‫davvero perché non lo stiamo effettivamente leggendo dal database.
‫Quindi implementiamo molto
‫rapidamente questo percorso qui,
‫ci vorrà solo un minuto.
‫Quindi, solo per vedere se questo qui funziona davvero.
‫Perché questo passaggio è
‫davvero importante per quello che faremo dopo, ok?
‫Quindi andiamo avanti e copiamo questo codice da getAllTours,
‫e non copiamo veramente, ma quello che faremo
‫è abbastanza simile a questo ovviamente.
‫Quindi copierò semplicemente questo,
‫ma senza le funzionalità API.
‫Quindi non mi interessa molto, ok?
‫E quindi questo lo
‫farò nel controller utente, perché ottenere tutti gli utenti non ha
‫nulla a che fare con l'autenticazione.
‫E quindi in questo caso
‫il controller utente è il posto perfetto per farlo.
‫Ora ovviamente ho anche bisogno di
‫importare rapidamente il modello utente qui, quindi
‫copiamo anche quello dal tourController, che non è lo stesso ma è simile.
‫Quindi prendiamo in realtà tutto questo nel
‫nostro controller utente.
‫Questo non ci serve, ma abbiamo anche bisogno di catchAsync.
‫Ok, quindi tour sarà
‫user, e qui sarà userModel, va bene?
‫Poi qui abbiamo i tour, e quaggiù e quaggiù,
‫quindi premo Comando + D per selezionarli
‫tutti e cambiarli in utenti.
‫E poi qui ovviamente quello che stiamo aspettando è Utente. trova.
‫Ora, poiché abbiamo
‫un'attesa qui, abbiamo bisogno di async, quindi abbiamo anche bisogno di next, e
‫poiché abbiamo un async
‫qui, dovremmo quindi avvolgere l'intera cosa in un catch async.
‫Quindi facciamolo.
‫Ok, e quindi a
‫questo punto non è più una
‫novità per te, siamo già abituati a fare questo genere di cose, giusto?
‫Quindi, questo dovrebbe già funzionare a
‫questo punto, quindi proviamolo.
‫Quindi prendi tutti gli utenti, e ora in effetti vediamo
‫che la loro password non è inclusa nell'output.
‫E questo è importante perché in realtà, nel find,
‫quindi qui nell'AuthController, anche
‫questo qui ora non conterrà la password, ok?
‫E quindi l'output di
‫questo qui ora non conterrà nemmeno la password.
‫Ma abbiamo bisogno della password per verificare
‫se è corretta, giusto?
‫E quindi dobbiamo anche selezionarlo esplicitamente.
‫Quindi ricorda come abbiamo usato select prima
‫per selezionare semplicemente un paio di
‫campi dal database, solo quelli di cui avevamo bisogno?
‫Ora in questo caso, quando vogliamo
‫che il campo che è di default non
‫selezionato, abbiamo bisogno di user plus e poi il nome del campo.
‫Quindi password in questo caso.
‫E così in questo modo, tornerà nell'output, ok?
‫Ovviamente dobbiamo attendere questa query
‫e quindi contrassegnare la funzione come asincrona.
‫E poi,
‫proprio come prima, per evitare quel brutto
‫blocco try catch, avvolgere l'intera funzione in catchAsync.
‫Va bene, ha senso?
‫Quindi ora registriamo rapidamente l'utente sulla console, solo per
‫vedere se funziona.
‫Quindi questo qui.
‫Otteniamo il nostro successo e in effetti otteniamo l'utente qui.
‫Quindi con esattamente l'e-mail che ho appena pubblicato,
‫e poi anche la password è tornata ad essere
‫inclusa nell'output, va bene?
‫E così di nuovo, è perché l'abbiamo selezionato esplicitamente qui.
‫E quindi ora è il momento di confrontare
‫effettivamente le password che abbiamo nel database con
‫quella che l'utente ha appena pubblicato.
‫Ma come lo faremo?
‫Perché ad esempio, la password
‫potrebbe essere, o è in questo esempio, pass1234,
‫ma quella che abbiamo memorizzato nel documento ha questo aspetto.
‫Allora come lo confrontiamo?
‫Non c'è davvero un modo per farlo, giusto?
‫Ma in realtà c'è, tutto ciò
‫che dobbiamo fare è usare di nuovo il pacchetto bcrypt, ok?
‫Quindi abbiamo usato bcrypt per generare questa password con hash
‫e possiamo anche usare lo stesso
‫pacchetto per confrontare sostanzialmente una password originale come questa qui con la
‫password con hash.
‫Ovviamente questa password qui, poiché è crittografata, non c'è
‫modo di recuperare la vecchia,
‫quindi la password originale da questa stringa, giusto?
‫Quindi questo è il
‫punto centrale della crittografia di una password.
‫Quindi l'unico modo
‫per farlo è che questo pacchetto
‫per questo algoritmo crittografi effettivamente anche questa
‫password e poi la confronti con quella crittografata, ok?
‫Quindi implementiamo una funzione che lo faccia,
‫e per questo useremo, di nuovo, il pacchetto bcrypt.
‫E lo faremo nel modello utente.
‫E potresti chiedere "Perché lo stiamo facendo
‫in un modello "e non solo qui", ma è,
‫ancora una volta, perché questo è davvero correlato ai dati stessi.
‫E inoltre abbiamo già quel pacchetto lì dentro,
‫quindi è più facile farlo semplicemente lì.
‫Quindi sbarazziamoci di questa stringa e poi
‫creiamo una funzione qui,
‫che controllerà se la password data
‫è la stessa di quella memorizzata nel documento.
‫Quindi per la prima volta ora creeremo
‫qualcosa chiamato metodo di istanza.
‫Quindi un metodo di istanza è fondamentalmente un metodo
‫che sarà disponibile su tutti i documenti
‫di una certa raccolta, ok?
‫E funziona così.
‫Quindi di nuovo, è definito
‫su un userSchema, quindi diciamo metodi, e ora in questo
‫caso vogliamo chiamare la funzione CorrectPassword, va bene?
‫Quindi funzione, ora questa funzione accetterà una
‫password candidate, quindi la
‫password che l'utente passa nel
‫corpo, e poi anche la password utente, ok?
‫Ora all'interno di questi metodi
‫istanziati, poiché sono disponibili nel documento,
‫la parola chiave this punta effettivamente al documento corrente.
‫Ma in questo caso, dal
‫momento che abbiamo la password impostata per selezionare false, quindi questo qui, ricordi?
‫Ok, e
‫per questo, questo. la password non sarà disponibile.
‫Quindi idealmente lo faremmo in questo modo, e
‫quindi in questo
‫modo dovremmo solo passare la candidatePassword e non la userPassword.
‫Ma ancora una volta, in questo
‫momento non è possibile perché la password non è disponibile nell'output.
‫Ed ecco
‫perché dobbiamo effettivamente passare anche userPassword.
‫Quindi l'obiettivo di
‫questa funzione è restituire solo vero o falso.
‫Quindi sostanzialmente vero se le password sono le stesse
‫e false in caso contrario.
‫Quindi ritorna, e poi bcrypt che già conosciamo, e
‫poi useremo la funzione di confronto, ok?
‫E la funzione di confronto è
‫davvero semplice, tutto ciò di cui abbiamo
‫bisogno è passare la candidatePassword e la userPassword, non userSchema, userPassword, ok?
‫E proprio come la funzione hash
‫qui sopra, anche questa è una funzione asincrona.
‫E così, proprio come prima, usiamo wait, e poi qui async.
‫Ok, ha senso?
‫Quindi, di nuovo, questa funzione di confronto
‫qui restituirà semplicemente true se
‫queste due password qui sono le stesse e false in caso contrario.
‫E ancora, non possiamo confrontarli
‫manualmente perché la password candidata non è
‫sottoposta ad hashing, quindi è la password
‫originale proveniente dall'utente, ma userPassword è
‫ovviamente sottoposta ad hashing, quindi senza
‫questa funzione qui non avremmo modo di confrontarli, ok?
‫Quindi qui restituiamo true o false,
‫ora tutto ciò che dobbiamo fare è chiamare effettivamente
‫questa funzione in authController.
‫Chiudiamo un paio di questi.
‫Va bene, quindi questo qui era solo per dimostrazione.
‫Quindi chiamiamolo semplicemente qui corretto, e
‫ora ricordiamo che la funzione che abbiamo appena definito
‫è un metodo istanziato.
‫E quindi è disponibile su tutti i documenti utente.
‫E quindi questa variabile qui in questo
‫momento è un documento utente, giusto?
‫Perché è il risultato dell'interrogazione del modello utente.
‫E quindi ora possiamo dire utente. password corretta.
‫Ora tutto ciò che dobbiamo fare è passare la password
‫candidata, che è password, ricorda.
‫Quindi questo qui e poi userPassword.
‫E quindi questo è in utente. password, ok?
‫E quindi questo qui ora sarà vero o falso.
‫Va bene, e ora usiamo effettivamente queste due
‫variabili per capire se l'utente esiste e
‫la password è corretta.
‫Quindi l'abbiamo già
‫capito, ma ora dobbiamo effettivamente scrivere la nostra istruzione if.
‫Quindi, se non c'è un utente, o la password non è corretta, quindi
‫sostanzialmente corretto è falso, che è ciò che significa.
‫In tal caso, vogliamo, ancora una volta, tornare
‫e passare direttamente al nostro prossimo middleware con un
‫nuovo AppError.
‫E in questo caso dire email o password errate, ok?
‫E poi il codice di
‫stato è 401, che significa non autorizzato, ok?
‫Ora avremmo potuto farlo separatamente,
‫quindi prima controlla l'utente
‫e poi controlla la password corretta.
‫Ma in tal caso forniremmo a un
‫potenziale aggressore informazioni se l'e-mail o la password non sono corrette.
‫E in questo modo qui è un po' più vago.
‫Quindi non stiamo davvero specificando cosa non è corretto qui.
‫Quindi se è email o se è la password.
‫Quindi, se c'è un
‫utente malintenzionato che cerca di inserire dati
‫casuali, non saprà se l'e-mail esiste effettivamente
‫o se è solo la password che è sbagliata, ok?
‫Ora solo due cose qui, e la
‫prima è che dobbiamo
‫effettivamente attendere questa funzione asincrona, ok?
‫Quindi ricorda che la password corretta
‫è una funzione
‫asincrona, quindi anche qui dobbiamo aspettare.
‫E poi c'è anche un
‫altro problema, perché questo utente non esiste
‫qui, quindi questa prossima riga di codice non può davvero essere eseguita, ok?
‫Perché, ad esempio, utente. la password
‫non sarà disponibile.
‫E quindi abbiamo effettivamente bisogno di spostare tutto
‫questo, o in realtà
‫solo questo codice, lo sposteremo qui nell'istruzione if else.
‫Va bene, quindi in questo modo,
‫se l'utente non esiste, quindi se questo è vero, allora
‫non eseguirà nemmeno questo codice qui, e
‫quindi non ci saranno problemi.
‫Ma se l'utente esiste, eseguirà anche questo codice e
‫verificherà se la password è effettivamente corretta.
‫E quindi se la password
‫è corretta, solo in quel caso raggiungiamo questo pezzo di codice qui.
‫Quindi questa è l'idea di questa funzione.
‫Quindi fondamentalmente controlliamo gli aspetti negativi.
‫Quindi, se non ci sono e-mail e
‫password, otteniamo questo errore.
‫Se non c'è nessun utente o se c'è una
‫password errata, crea questo errore.
‫Ma se non ci fosse
‫alcun errore, in tal caso raggiungiamo questa parte del
‫codice, dove ora generiamo
‫un token e poi lo rimandiamo all'utente.
‫Ora creare questo token sarà esattamente la stessa cosa che
‫abbiamo fatto prima qui.
‫E quindi invece di ripetere
‫tutto questo codice, creiamo effettivamente una funzione per questo, ok?
‫Quindi copierò
‫questo codice, e poi qui molto rapidamente
‫const signToken, che riceverà come unico input l'ID utente.
‫Quindi ID, e poi restituirà il token, ok?
‫Quindi firmerà semplicemente questo e lo restituirà immediatamente.
‫Va bene, quindi quello che
‫faremo è firmare il token e passare questo documento d'identità.
‫E poi, ovviamente, dobbiamo anche cambiarlo qui, ok?
‫Quindi imposta questo ID su questo ID, che
‫come già sai a questo punto è lo stesso di questo.
‫Va bene, quindi ora andiamo avanti e facciamo lo stesso qui.
‫signToken, e quindi in questo caso è user. _ID.
‫Uff, è stata una
‫lunga conferenza, ora andiamo avanti e proviamolo.
‫Quindi ora dovrebbe funzionare.
‫Quindi ricorda, questo è ora un altro utente, ma so che questa
‫è la password che ho usato.
‫Quindi per ora usiamone un
‫altro, quindi test12 e vediamo cosa succede.
‫Aspettiamo, e in effetti, email o password errate.
‫E così otteniamo ovviamente anche il nostro
‫401, e in effetti significa che il nostro codice sta già
‫testando la password.
‫Quindi, se ora scegliamo la password corretta,
‫dovrebbe passare, quindi aspettiamo.
‫Ma riceviamo ancora questo errore.
‫Bene, quindi liberiamoci semplicemente di tutti i nostri utenti qui,
‫solo per assicurarci che non debba
‫fare nulla con gli utenti, e poi creiamone uno nuovo, va bene.
‫Quindi ciao@jonas e passa1234.
‫Andiamo avanti e copiamolo, lo mando subito.
‫E quindi queste credenziali ora sono sicuramente corrette.
‫Quindi, se ora non funziona, c'è un qualche tipo di bug.
‫Ma ora in realtà ha funzionato, alla grande.
‫Quindi ecco il nostro token, e sembra esattamente come prima, quindi
‫possiamo presumere che sia corretto.
‫Ora proviamo di nuovo, cancellando questo, ok.
‫E quindi con la password sbagliata non funziona.
‫Ora mettiamo la password giusta e l'e-mail sbagliata, e poi
‫otteniamo lo stesso errore.
‫Quindi email o password errate.
‫E ancora, se rimettiamo tutto a posto,
‫otteniamo un nuovo token.
‫E così ora siamo davvero registrati nell'applicazione.
‫E so che questo potrebbe sembrare un po'
‫astratto, quindi tutto ciò che otteniamo davvero è un token, e
‫questo significa che siamo registrati nell'applicazione, giusto?
‫Ma, beh, è così che funziona con l'autenticazione senza stato.
‫In seguito, quando creeremo davvero il sito
‫Web dinamico, ovviamente sarà molto
‫più visibile se l'utente è connesso o disconnesso, giusto?
‫Ma funzionerà ancora dietro le quinte
‫utilizzando lo stesso token.
‫Quindi, se non ci sono token, il sito Web avrà un aspetto
‫in un modo, e se c'è
‫un token, il sito Web avrà un aspetto diverso,
‫con l'immagine dell'utente e il nome utente proprio lì nel sito Web, ad esempio.
‫Comunque, questa è stata una conferenza piuttosto lunga.
‫Assicurati di tornare qui e cerca davvero
‫di capire tutto questo codice che abbiamo qui, va bene?
‫Perché so che alcune di
‫queste potrebbero essere un po' confuse.
‫Ad esempio, questa funzione CorrectPassword qui.
‫E poi sono sicuro che tutto avrà un senso per te.