‫Jonas: Então,
‫nesta aula, vamos implementar a funcionalidade
‫de login de usuários com base em uma determinada senha
‫e endereço de e-mail.
‫E, assim como antes, o conceito de login de um usuário
‫basicamente significa assinar um token da Web JSON
‫e enviá-lo de volta ao cliente.
‫Mas, neste caso, só emitimos o
‫token caso o usuário realmente exista
‫e a senha esteja correta.
‫Então, vamos começar a implementar isso.
‫Exportações. login é ...
‫solicitar, responder e em seguida.
‫E a primeira coisa é que devemos
‫realmente ler o e-mail e a senha do corpo.
‫Então, digamos que const email é igual a req. corpo. o email.
‫Ok, e agora o eslint nos dá esse aviso, ou
‫na verdade esse erro, e
‫diz que devemos usar a desestruturação de objetos, certo?
‫E então eu deixo isso acontecer aqui de
‫propósito apenas para que eu possa mostrar como podemos
‫fazer isso com a desestruturação ES6.
‫E acho que falei sobre isso antes no curso e
‫também espero que você já esteja familiarizado
‫com o conceito, mas de qualquer
‫maneira, como o nome
‫desta propriedade aqui é o mesmo que
‫o nome da variável, podemos simplesmente
‫fazer como isso e livre-se do nome da propriedade aqui, ok?
‫E também queremos obter a senha.
‫E isso seria o mesmo, então,
‫basicamente, a senha é igual a uma solicitação. corpo. senha.
‫E em
‫vez de fazer isso, podemos simplesmente enviar e-mail e senha.
‫E assim, basicamente
‫criamos essas duas variáveis do objeto body, ok?
‫Basicamente, é assim que
‫o usuário enviará as credenciais de login para que possamos verificar.
‫Ok, e esse processo de verificação tem algumas etapas,
‫então vamos colocá-las aqui
‫antes de começar a codificar.
‫Portanto, primeiro precisamos verificar se
‫o e-mail e as senhas realmente existem, certo?
‫Em seguida, número dois, verifique se o usuário existe,
‫e ao mesmo tempo se a senha está correta.
‫E, em terceiro lugar, se tudo
‫estiver bem, envie o token, ou seja, o token da Web JSON de volta ao cliente.
‫Ok, então o primeiro é muito fácil.
‫Portanto, se não houver e-mail ou senha, queremos enviar uma
‫mensagem de erro ao nosso cliente.
‫Agora, como vamos fazer isso?
‫Bem, vamos fazer isso
‫usando as ferramentas que implementamos na última seção.
‫Então, basicamente, nosso erro de aplicativo, lembra disso?
‫Portanto, vamos simplesmente criar um novo erro
‫aqui, e nosso middleware de tratamento
‫de erros global irá pegá-lo e enviar esse erro de volta ao cliente.
‫E então vamos começar importando esse erro.
‫Então lembre-se que é
‫este aqui da nossa classe AppError, certo?
‫Então, digamos AppError e, em seguida, require.
‫Então, um nível acima daqui, em seguida, nos utilitários e, em
‫seguida, appError.
‫Ok, então precisamos chamar o próximo middleware,
‫e então é aqui que passamos o erro, lembra disso?
‫Então, novo AppError, e
‫a mensagem é: forneça e-mail e senha.
‫E o código de erro HTTP é 400 para solicitação incorreta, certo?
‫E agora eu tenho
‫alguns erros aqui, e isso novamente está me ajudando a consertar alguns bugs.
‫Então, vejo que aqui
‫em cima, novamente, usei-o com um T em vez
‫de D, e se eu salvá-lo agora, está tudo correto.
‫Ok, vamos começar a testar isso agora mesmo.
‫E, para isso,
‫vou simplesmente começar criando um token falso aqui por enquanto, ok.
‫E então vamos dizer res. status 200 para ok.
‫JSON e, claro, como
‫sempre, o status definido como sucesso e também nosso token.
‫E isso é tudo o
‫que vamos enviar como resposta ao login, ok?
‫Portanto, nenhum objeto de usuário, porque isso não é necessário.
‫Tudo o que queremos como resposta para o login é
‫na verdade o token.
‫Isso é tudo que importa quando o usuário faz login.
‫Em seguida, precisamos implementar a rota,
‫então vamos fazer isso novamente em nosso roteador de usuário, ok?
‫E este é realmente muito semelhante a este.
‫Vamos apenas duplicá-lo e, em seguida,
‫substituir a inscrição em ambas as instâncias por login, certo?
‫E, novamente, isso só é válido para uma solicitação de
‫postagem, porque é claro que
‫queremos enviar as credenciais de login no corpo.
‫E então novamente é
‫um post, mas não um get, não um patch, e
‫não um delete, porque isso não faz sentido neste caso.
‫Então, vamos testar agora.
‫Vou copiar este URL aqui também.
‫Vai ser postar, fazer login e depois o corpo,
‫é claro.
‫Então, novamente, raw e JSON.
‫Então, vamos criar um corpo simples aqui.
‫Tudo bem, então vamos tentar
‫enviar sem senha e ver o que acontece.
‫E de fato funciona, então recebemos nossa mensagem
‫aqui: forneça e-mail e senha.
‫E, novamente, como estamos em
‫desenvolvimento, estamos obtendo toda essa pilha de
‫erros aqui, e o erro completo com nosso código
‫de status, o status e também, é claro,
‫dizendo que é um erro operacional.
‫Assim como definimos na seção anterior, ok?
‫Agora vamos tentar o mesmo com a senha.
‫Então, a senha, defina-a como Acho que é essa e, em
‫seguida, livre-se do e-mail.
‫E isso, claro, aciona o mesmo.
‫Ok, e com tudo correto, isso deve funcionar e,
‫de fato, recebemos nosso token falso aqui.
‫Agora, apenas uma coisa que devemos fazer
‫aqui é retornar também.
‫Então, já fizemos isso muitas vezes
‫antes, e deixe-me explicar novamente.
‫É simplesmente porque depois de chamar o
‫próximo middleware, queremos ter certeza de
‫que esta função de login aqui termine imediatamente, certo?
‫E como não tínhamos isso
‫antes, agora você vê esse erro aqui novamente.
‫Então, isso já é meio familiar neste ponto, certo?
‫Portanto, não é possível definir cabeçalhos depois de serem enviados
‫ao cliente, porque, novamente, enviamos duas respostas.
‫Primeiro a resposta de erro e, em seguida, também este código aqui executado, o
‫que obviamente não deveria ter acontecido.
‫E então, novamente, usamos return aqui para consertar isso.
‫Ok, em seguida, vamos verificar se realmente existe um usuário
‫para o e-mail que foi postado.
‫Então const user, e agora vamos usar findOne na verdade, porque
‫desta vez não estamos selecionando um usuário pelo
‫ID, mas pelo seu e-mail, certo?
‫E então precisamos passar aquele
‫objeto de filtro onde podemos dizer e-mail igual a e-mail, ok?
‫Portanto, o campo é chamado de
‫e-mail e a variável também é chamada de e-mail.
‫E assim, no ES6, podemos abreviar isso simplesmente como isso.
‫Antes de prosseguirmos
‫aqui, há algo importante
‫que preciso fazer para a segurança.
‫Deixe-me mostrar isso para você no
‫Postman, onde nos inscrevemos para uma nova conta.
‫E então você vê aqui que na
‫saída do usuário, nós realmente obtemos a senha, ok?
‫Na verdade, está criptografado, mas
‫ainda assim não é uma boa prática vazar
‫os dados da senha para o cliente, certo?
‫Por exemplo, se tivéssemos nosso get todos os usuários
‫aqui, e esta rota ainda não
‫foi implementada, mas se fôssemos obter todos os usuários da
‫coleção, então todos eles teriam
‫a senha visível, e não queremos isso , OK?
‫E então consertar é bem fácil, porque já
‫tínhamos feito isso antes.
‫Tudo o que precisamos fazer
‫é dizer a senha e, em seguida, selecioná-la e defini-la como falsa.
‫E assim, ele nunca aparecerá automaticamente em
‫nenhuma saída.
‫E para me deixar
‫realmente provar isso para você, inscrevendo-se como outra coisa, algo assim.
‫E vamos ver, ah, na verdade já temos
‫um usuário com aquele e-mail, então vamos apenas fazer o test3.
‫E agora a senha ainda está aqui, mas acho
‫que é simplesmente porque poderíamos simplesmente criá-la, este
‫novo documento aqui,
‫e portanto não está realmente selecionando
‫porque não estamos realmente lendo do banco de dados.
‫Então, vamos implementar
‫muito rapidamente essa rota
‫aqui, isso só levará um minuto.
‫Então, só para ver se isso aqui realmente funciona.
‫Porque esta etapa é realmente
‫importante para o que faremos a seguir, certo?
‫Então, vamos seguir em frente e copiar este código de
‫getAllTours, e não copiar realmente, mas o que faremos
‫é bastante semelhante a isso, é claro.
‫Vou simplesmente copiar isso,
‫mas sem os recursos da API.
‫Não estou realmente interessado nisso, certo?
‫E então este eu
‫vou fazer no controlador de usuário, porque obter todos os usuários
‫não tem nada a ver com autenticação.
‫E então, neste caso, o
‫controlador do usuário é o lugar perfeito para fazer isso.
‫Agora é claro que também preciso
‫importar rapidamente o modelo de usuário aqui, então
‫vamos também copiar isso do tourController, que não é o mesmo, mas semelhante.
‫Então, vamos colocar tudo isso em nosso
‫controlador de usuário.
‫Este nós não precisamos, mas também precisamos catchAsync.
‫Ok, então o tour
‫será user, e aqui será userModel, certo?
‫Então aqui temos tours, e aqui e aqui embaixo, então
‫eu pressiono Command + D para selecionar
‫todos eles e mudar para usuários.
‫E então aqui é claro o que estamos aguardando é o usuário. achar.
‫Agora, uma vez
‫que temos um await aqui, precisamos async, então também precisamos em seguida, e
‫já que temos uma
‫async aqui, devemos agrupar tudo em um catch async.
‫Então vamos fazer isso.
‫Ok, e neste ponto
‫isso não é mais nenhuma novidade
‫para você, já estamos acostumados a fazer esse tipo de coisa, certo?
‫Então, isso já deve funcionar neste
‫ponto, então vamos tentar.
‫Portanto, pegue todos os usuários e, agora, de fato, vemos
‫que suas senhas não estão incluídas na saída.
‫E isso é importante porque na verdade, no find, então
‫de volta aqui no AuthController,
‫isso aqui agora também não conterá a senha, ok?
‫E então a saída
‫disso aqui agora também não conterá a senha.
‫Mas precisamos da senha para verificar se
‫ela está correta, certo?
‫E então precisamos selecioná-lo explicitamente também.
‫Portanto, lembre-se de como usamos select antes
‫para simplesmente selecionar alguns campos
‫do banco de dados, apenas os que precisávamos?
‫Agora, neste caso, quando queremos o
‫campo que está por padrão não selecionado,
‫precisamos do usuário plus e, em seguida, o nome do campo.
‫Portanto, senha neste caso.
‫E assim assim, vai voltar na saída, ok?
‫É claro que precisamos aguardar essa
‫consulta e, em seguida, marcar a função como assíncrona.
‫E então,
‫como antes, para evitar aquele feio bloco
‫try catch, envolva toda a função em catchAsync.
‫Tudo bem, faz sentido?
‫Portanto, vamos rapidamente registrar o usuário no console, apenas para
‫ver se funciona.
‫Então, este aqui.
‫Conseguimos nosso sucesso e, de fato, colocamos o usuário aqui.
‫Então, exatamente com o e-mail que acabei de
‫postar, e a senha agora também voltou a ser
‫incluída na saída, certo?
‫E então, novamente, isso é porque o selecionamos explicitamente aqui.
‫E agora é hora de realmente comparar as
‫senhas que temos no banco de dados com
‫as que o usuário acabou de postar.
‫Mas como vamos fazer isso?
‫Porque, por exemplo, a senha
‫pode ser, ou é neste exemplo, pass1234,
‫mas aquela que armazenamos no documento tem esta aparência.
‫Então, como vamos comparar isso?
‫Não há realmente uma maneira de fazer isso, certo?
‫Mas na verdade existe, tudo o
‫que temos que fazer é usar novamente o pacote bcrypt, ok?
‫Portanto, usamos bcrypt para gerar essa senha com hash e
‫também podemos usar o mesmo pacote
‫para comparar basicamente uma senha original como esta aqui com a
‫senha com hash.
‫Claro essa senha aqui, já que está criptografada,
‫não tem como voltar a
‫antiga, então a senha original dessa string, certo?
‫Então esse é o
‫ponto principal de realmente criptografar uma senha.
‫E então a
‫única maneira de fazer isso é
‫para este pacote para este algoritmo criptografar
‫essa senha também, e então compará-la com a criptografada, certo?
‫Então, vamos implementar uma função que fará
‫isso e, para isso, usaremos, novamente, o pacote bcrypt.
‫E faremos isso no modelo de usuário.
‫E você pode perguntar "Por que estamos fazendo
‫isso em um modelo" e não apenas aqui ",
‫mas isso é, novamente, porque isso está realmente relacionado aos próprios dados.
‫E também já temos esse pacote lá, então
‫é mais fácil simplesmente fazer lá.
‫Então, vamos nos livrar dessa string e, em
‫seguida, criar uma função
‫aqui, que irá verificar se a senha
‫fornecida é a mesma que está armazenada no documento.
‫Então, pela primeira vez, vamos criar algo
‫chamado método de instância.
‫Portanto, um método de instância é basicamente um método
‫que estará disponível em todos os documentos
‫de uma determinada coleção, certo?
‫E funciona assim.
‫Então, novamente, é definido
‫em um userSchema, e então dizemos métodos e agora,
‫neste caso, queremos chamar a função correctPassword, certo?
‫Então função, agora essa função vai aceitar
‫um candidatePassword, então a
‫senha que o usuário passa
‫no corpo, e depois também o userPassword, ok?
‫Agora, dentro desses métodos instanciados,
‫uma vez que estão disponíveis no documento,
‫a palavra-chave this realmente aponta para o documento atual.
‫Mas nesse caso, já que
‫temos a senha configurada para selecionar false, então isso aqui, lembra?
‫Ok, e
‫por causa disso, isso. a senha não estará disponível.
‫Idealmente, faríamos assim e, dessa forma,
‫precisaríamos apenas
‫passar a senha candidata e não a senha_de_usuário.
‫Mas, novamente, agora isso não é
‫possível porque a senha não está disponível na saída.
‫E é
‫por isso que também temos que passar a userPassword.
‫Portanto, o objetivo
‫desta função é realmente retornar apenas verdadeiro ou falso.
‫Portanto, basicamente verdadeiro se as senhas forem iguais e
‫falso se não forem.
‫Então volte e bcrypt que já sabemos, e então
‫vamos usar a função de comparação, ok?
‫E a função de comparação é realmente
‫fácil, tudo o que precisamos é passar a
‫senha do candidato e a senha do usuário, não userSchema, senha do usuário, certo?
‫E, assim como a função
‫hash aqui, esta também é uma função assíncrona.
‫E assim como antes de usar o await, e aqui assíncrono.
‫Ok, faz sentido?
‫Portanto, novamente, esta função de comparação aqui
‫simplesmente retornará true se
‫essas duas senhas aqui forem iguais e false se não forem.
‫E, novamente, não podemos
‫compará-los manualmente porque a senha candidata não
‫é hash, então é a senha
‫original vindo do usuário, mas userPassword
‫é, é claro, hash, e sem
‫esta função aqui não teríamos como compará-los, ok?
‫Portanto, aqui retornamos verdadeiro ou falso,
‫agora tudo o que precisamos fazer é realmente chamar
‫esta função no authController.
‫Vamos fechar alguns deles.
‫Tudo bem, então isso aqui estava aqui apenas para demonstração.
‫Então, vamos simplesmente chamar isso de correto
‫e agora lembrar que a função que acabamos de
‫definir é um método instanciado.
‫E, portanto, está disponível em todos os documentos do usuário.
‫E então essa variável aqui agora é
‫um documento do usuário, certo?
‫Porque é o resultado da consulta ao modelo do usuário.
‫E agora podemos dizer usuário. correctPassword.
‫Agora tudo o que precisamos fazer é passar a senha do
‫candidato, que é a senha, lembre-se.
‫Então, este aqui, e então a userPassword.
‫E isso está no usuário. senha, certo?
‫E então isso aqui agora será verdadeiro ou falso.
‫Certo, agora vamos usar essas duas variáveis para
‫descobrir se o usuário existe e a
‫senha está correta.
‫Já descobrimos isso,
‫mas agora precisamos realmente escrever nossa instrução if.
‫Portanto, se não houver usuário ou a senha estiver incorreta, basicamente correto
‫é falso, que é o que isso significa.
‫Nesse caso, queremos, novamente, retornar e ir
‫direto para nosso próximo middleware com um novo
‫AppError.
‫E nesse caso dizendo e-mail ou senha incorretos, ok?
‫E então o código de status
‫é 401, o que significa não autorizado, certo?
‫Agora poderíamos ter feito isso separadamente,
‫primeiro verificando o usuário
‫e, em seguida, verificando a senha correta.
‫Mas, nesse caso, forneceríamos a um invasor
‫potencial informações se o e-mail ou a senha estão incorretos.
‫E assim aqui fica um pouco mais vago.
‫Portanto, não estamos realmente especificando o que está incorreto aqui.
‫Então, se é e-mail ou se é a senha.
‫E se houver algum
‫invasor tentando inserir alguns dados aleatórios, eles
‫não saberão se o e-mail realmente existe
‫ou se é apenas a senha que está errada, certo?
‫Agora, apenas duas coisas aqui, e a
‫primeira é que realmente
‫precisamos aguardar essa função assíncrona, certo?
‫Portanto, lembre-se de que a senha correta
‫é uma função assíncrona
‫e, portanto, aqui também precisamos aguardar por isso.
‫E também tem outro
‫problema, porque esse usuário não existe aqui,
‫então essa próxima linha de código não pode realmente ser executada, ok?
‫Porque, por exemplo, usuário. a senha
‫não estará disponível.
‫E então, na verdade, precisamos mover tudo isso,
‫ou apenas esse
‫código, vamos movê-lo aqui para a instrução if else.
‫Tá bom, e assim assim, se
‫o usuário não existir, então se for verdade, então não
‫vai nem rodar esse código aqui,
‫aí não vai ter problema.
‫Mas se o usuário existir, ele também executará este código
‫e verificará se a senha está realmente correta.
‫E se a senha
‫estiver correta, só nesse caso chegaremos a esse pedaço de código aqui.
‫Então essa é a ideia geral dessa função.
‫Então, basicamente, verificamos os negativos.
‫Portanto, se não houver nenhum e-mail e
‫nenhuma senha, obteremos este erro.
‫Se não houver nenhum usuário ou se houver uma
‫senha errada, crie este erro.
‫Mas se não houve nenhum erro,
‫bem, nesse caso chegamos a esta parte do código, onde
‫agora geramos um token
‫e o enviamos de volta para o usuário.
‫Agora, criar esse token será exatamente a mesma coisa
‫que fizemos antes aqui.
‫E então, em vez de repetir
‫todo esse código, vamos criar uma função para isso, ok?
‫Vou copiar
‫esse código e, em seguida, const signToken,
‫que receberá como única entrada o ID do usuário.
‫Então ID, e ele retornará o token, certo?
‫Portanto, ele simplesmente assinará e retornará imediatamente.
‫Tudo bem, então aqui o
‫que vamos fazer é assinarToken e passar esta ID.
‫E é claro que também precisamos mudar isso aqui, ok?
‫Portanto, defina este ID com este ID, que
‫como você já sabe neste ponto, é o mesmo que este.
‫Tudo bem, então vamos agora fazer o mesmo aqui.
‫signToken e, neste caso, é o usuário. _identificação.
‫Ufa, foi uma
‫longa palestra, vamos agora experimentar.
‫Portanto, agora deve funcionar.
‫Então lembre-se, bem, este é agora outro usuário, mas eu sei que
‫esta é a senha que usei.
‫Então, por enquanto, vamos usar outro,
‫então apenas test12, e vamos ver o que acontece.
‫Vamos esperar, e de fato, e-mail ou senha incorretos.
‫Obviamente, obtemos também nosso 401 e,
‫de fato, isso significa que nosso código já está testando
‫a senha.
‫Portanto, se agora escolhermos a senha correta, ela deve
‫passar, então vamos esperar por isso.
‫Mas, ainda recebemos esse erro.
‫Bem, então vamos simplesmente nos livrar de todos os nossos usuários aqui,
‫apenas para ter certeza de que não
‫há nada a ver com os usuários, e então criar um novo, tudo bem.
‫Então, olá @ jonas e pass1234.
‫Vamos copiar, vou mandar agora.
‫E então essas credenciais agora estão corretas com certeza.
‫Então, se não funcionar agora, há algum tipo de bug.
‫Mas agora funcionou, ótimo.
‫Então, aqui está nosso token, e ele se parece exatamente com antes, então
‫podemos assumir que ele está correto.
‫Agora vamos tentar novamente, deletando isso, ok.
‫E assim com a senha errada não funciona.
‫Agora vamos colocar a senha certa e o e-mail errado, e então
‫obteremos o mesmo erro.
‫E-mail ou senha tão incorretos.
‫E, novamente, se colocarmos tudo de volta,
‫obteremos um novo token.
‫E agora estamos realmente logados no aplicativo.
‫E eu sei que isso pode parecer meio
‫abstrato, então tudo o que realmente obtemos é um token, e isso
‫significa que estamos logados no aplicativo, certo?
‫Mas, bem, é assim que funciona na autenticação sem estado.
‫Mais tarde, quando realmente construirmos o site dinâmico,
‫é claro que ele ficará muito
‫mais visível se o usuário estiver conectado ou desconectado, certo?
‫Mas ainda funcionará nos bastidores
‫usando esse mesmo token.
‫Então, se não houver um token, o site terá uma aparência,
‫e se houver um token, o
‫site terá uma aparência diferente, com a imagem
‫do usuário e o nome de usuário ali mesmo no site, por exemplo.
‫De qualquer forma, esta foi uma palestra bastante longa.
‫Certifique-se de voltar aqui e realmente tentar
‫entender todo esse código que temos aqui, certo?
‫Porque eu sei que algumas
‫dessas coisas podem ser um pouco confusas.
‫Por exemplo, esta função correctPassword aqui.
‫E então tenho certeza que tudo fará sentido para você.