‫Instrutor: E agora vamos finalmente criar a última parte
‫da Funcionalidade de Redefinição de
‫Senha, onde realmente definimos a nova senha para o usuário.
‫E então, assim como antes, vamos começar definindo
‫as etapas que iremos realizar para este fluxo de resetPassword.
‫Portanto, primeiro, obtenha o usuário com base no token.
‫Em seguida, como segunda etapa, definiremos a nova senha, mas
‫apenas se o token não tiver expirado e
‫houver um usuário.
‫Nesse caso, defina a nova senha.
‫Depois disso, precisamos atualizar a propriedade changedPasswordAt
‫para o usuário atual e, finalmente, como
‫é normal nesta
‫funcionalidade, é fazer o login do
‫usuário.
‫Basicamente, envie o JSON Web Token ao cliente.
‫Ok, muito trabalho a fazer, então vamos começar.
‫E então, lembre-se do último vídeo, que o token
‫de redefinição que é realmente enviado na URL
‫é esse token não criptografado aqui.
‫Então, na verdade este.
‫Mas o que temos no banco
‫de dados é o criptografado.
‫Falamos sobre isso antes e, portanto, o que agora
‫precisamos fazer é basicamente criptografar o token
‫original novamente, para que
‫possamos compará-lo com aquele que está armazenado, ou
‫seja, o criptografado no banco de dados.
‫Então, nós realmente fizemos algo semelhante
‫antes com a senha, mas com a
‫senha, não poderíamos compará-la tão facilmente quanto podemos com
‫esta, novamente porque
‫para a senha usamos o algoritmo bcrypt bastante complexo,
‫que neste caso, nós não.
‫Portanto, aqui é muito simples.
‫Tudo o que precisamos fazer novamente é criptografar
‫o token e compará-lo com o criptografado no banco de dados.
‫Então, digamos hashedToken, e agora vamos precisar do
‫pacote crypto aqui também.
‫Const crypto e depois require ('crypto').
‫Agora está certo.
‫Então vamos voltar, e então, usamos
‫criptografia. createHash.
‫Lembre-se, então, do nome do algoritmo
‫novamente, sha256, então. atualizar, basicamente para o local, para a
‫string que queremos hash.
‫E assim, esse é, lembre-se no req. params.
‫Então, usamos este por um longo tempo. símbolo.
‫E então, novamente, é um
‫parâmetro, porque especificamos isso aqui na URL, assim.
‫Então, agora é um parâmetro chamado token.
‫E então, é claro, aqui está o req. params. símbolo.
‫E então, finalmente, também precisamos dizer digerir
‫e convertê-lo em hexadecimal.
‫Agora, isso é basicamente o mesmo
‫que tínhamos antes, onde criptografamos o original e,
‫portanto, poderíamos refatorá-lo em sua própria função, mas
‫vamos mantê-lo simples aqui.
‫Portanto, agora vamos realmente obter o usuário com base neste token.
‫Porque essa é, na verdade, a
‫única coisa que sabemos sobre o usuário no momento.
‫Não temos e-mail, não temos nada, então esse token é
‫a única coisa que pode identificar o usuário.
‫E agora podemos, basicamente, consultar o banco de
‫dados para este token.
‫Em seguida, ele encontrará o usuário que possui esse token.
‫Então, aguarde, como já sabemos, e depois Usuário. findOne.
‫Portanto, essa propriedade é chamada
‫de passwordResetToken e estamos procurando o hashedToken.
‫E agora, é claro, precisamos declará-lo como assíncrono e
‫prepará-lo para catchAsync.
‫Salve-o, isso deve corrigir o bug, e
‫de fato corrige.
‫Assim, ele encontrará o usuário que possui o
‫token que enviará via URL.
‫Mas, no momento, não estamos
‫levando em consideração a data de expiração do token.
‫E então, como poderíamos fazer isso?
‫Bem, basicamente, o que queremos é verificar
‫se a propriedade passwordResetExpires é maior
‫do que agora.
‫Porque se a data de validade for maior do
‫que agora, significa que está no futuro, o que por sua
‫vez significa que ainda não expirou.
‫E essa é uma maneira
‫muito fácil de fazermos isso da maneira certa com essa consulta.
‫Então, passwordResetExpires, que é onde essa
‫data é armazenada, e agora tudo o
‫que precisamos verificar
‫se ela é realmente maior do que agora.
‫E então já sabemos como fazer isso com o MongoDB, certo?
‫Então, novo objeto e então o maior operador e então o que queremos
‫compará-lo é a data. agora, e este será realmente
‫um registro de data e hora de agora,
‫mas nos bastidores, o MongoDB converterá tudo para o
‫mesmo e, portanto, será capaz de
‫compará-los com precisão.
‫E assim podemos, ao mesmo tempo,
‫encontrar o usuário para o token e também verificar se
‫o token ainda não expirou.
‫Tão bom.
‫Então, a seguir queremos, é claro, enviar um
‫erro se não houver usuário, ou basicamente, se o token tiver expirado.
‫Mas isso, neste caso,
‫é o mesmo, porque se o token expirou, então
‫ele simplesmente não retornará nenhum usuário.
‫E então tudo o que precisamos fazer é dizer,
‫se nenhum usuário, bem, então, como sempre, volte em seguida, isso não é mext.
‫Portanto, novo AppError, e digamos
‫que o Token é inválido ou expirou.
‫E então 400, pedido tão ruim.
‫E então, se não houver erro
‫e se next não
‫for chamado, vamos definir a senha.
‫Então, já pegamos o usuário e agora é muito
‫simples: usuário. a senha é igual a req. corpo. senha.
‫E isso porque, é claro, enviaremos a
‫senha e também a passwordConfirm por meio do corpo.
‫Então, vamos
‫duplicar isso e também passwordConfirm.
‫E também, vamos basicamente excluir o token de redefinição
‫e o expirado.
‫Portanto, passwordResetToken, assim como fizemos antes, definimos como undefined,
‫e agora user. a senha expira é igual
‫a indefinida.
‫Tudo bem.
‫E de novo, é claro, agora precisamos
‫salvá-lo, porque isso apenas modifica o documento, na verdade
‫não é atualizado.
‫Portanto, não o salva realmente.
‫Portanto, aguarde o usuário. Salve .
‫E, neste caso, não precisamos
‫desligar os validadores, porque realmente queremos validar.
‫Por exemplo, queremos que o validador
‫confirme se a senha é igual a passwordConfirm.
‫E então esse validador faz todo
‫esse trabalho automaticamente para nós.
‫Em seguida, a terceira etapa, o que realmente
‫faremos no final, e o que faremos a seguir
‫é basicamente bloquear o usuário.
‫Em outras palavras, envie o JSON Web Token.
‫E vamos pegar aquele código daqui, então este.
‫E, novamente, já estamos fazendo
‫isso aqui em três lugares diferentes.
‫Então aqui no login, também no cadastro, e agora
‫pela terceira vez, aqui embaixo.
‫E então, em algum momento no futuro, iremos refatorar isso em
‫sua própria função.
‫Mas, por enquanto, estamos bem assim.
‫E então, vamos realmente prosseguir e testar isso.
‫Portanto, esse token de
‫redefinição que tínhamos antes já expirou e,
‫basicamente, precisamos pedir um novo.
‫Então, vamos ao Postman e decidamos esquecer a senha.
‫Vamos apenas reduzir a desordem aqui
‫e nos livrar de todas essas guias abertas de que não
‫precisamos mais.
‫Na verdade, vamos precisar desse teste
‫para redefinir a senha, porque
‫lembre-se de que este realmente recebe um JSON
‫Web Token e, portanto, queremos salvá-lo na variável de
‫ambiente, assim como
‫fizemos com todos os outros.
‫Então, estou fazendo isso agora, só para não esquecer.
‫Tudo bem, de qualquer maneira, vamos começar basicamente
‫esquecendo a senha.
‫Então, o envio dessa solicitação, que de novo,
‫demora algumas vezes por causa
‫do envio do e-mail, mas vamos lá, vamos agora para o
‫nosso e-mail, que chegou há poucos segundos.
‫Então, é isso, é claro, esse token.
‫Então vamos pegá-lo, copiá-lo e agora voltar para o Postman, vamos
‫utilizá-lo no Reset Password, como a URL.
‫Ok, faz sentido?
‫Então, novamente, estamos enviando esse token direto no URL.
‫Então aqui, vamos especificar o
‫corpo, porque agora, precisamos realmente especificar nossa nova senha.
‫Então senha e digamos newpass.
‫E então...
‫E aqui vamos chamá-lo de
‫outra coisa, porque, por enquanto, na verdade, quero ver um erro.
‫E, claro, isso é chamado de passwordConfirm.
‫Então, vamos ver o que acontece quando tentamos fazer isso.
‫Vamos esperar, e obteremos uma senha menor que
‫o comprimento mínimo permitido.
‫Ok, então vamos mudar isso, 123, e aqui vamos dizer 1234.
‫Então, eu quero que eles sejam diferentes.
‫Mas você vê que a validação aqui funcionou bem,
‫mesmo ao atualizar a senha com salvar.
‫Então, e agora temos as senhas não são as mesmas!
‫Então, novamente, isso é um erro de validação.
‫E lembre-se, na verdade, que esta é a
‫razão pela qual precisamos usar o save e não update.
‫Então antes, para os
‫tours de atualização, costumávamos usar findOneAndUpdate, mas agora, para tudo
‫relacionado a senhas e ao usuário, sempre usamos
‫save, pois queremos sempre rodar todos os validadores,
‫e acima de tudo, as funções
‫de middleware save.
‫Assim, por exemplo, aqueles em que as senhas são criptografadas.
‫Então, vamos acabar com isso agora.
‫Oh, eu realmente não corrigi, desculpe por isso.
‫E agora deve realmente funcionar.
‫E, de fato, obtemos sucesso e recebemos um novo token.
‫Ótimo, vamos ver se esse token é realmente válido.
‫Então, se pudermos, obtenha todos os tours usando esse token totalmente novo.
‫E aqui vamos nós.
‫Portanto, nosso novo token realmente funciona, e agora para este usuário,
‫para hello @ jonas, essas duas
‫propriedades devem ter desaparecido.
‫Então a senha expira e o token deve ter sumido,
‫já que, bem, já que foi isso que fizemos em nosso código.
‫E então, sim, eles não estão mais aqui.
‫Agora, na verdade, tudo o que precisamos fazer é esta etapa que
‫falta aqui, que é atualizar a propriedade passwordAt para
‫este usuário atual.
‫Mas isso não
‫deve ser tão difícil, então vamos voltar rapidamente
‫ao userModel, que é onde faremos isso usando middleware.
‫E vamos realmente colocar todo o
‫middleware junto aqui no topo.
‫Então, userSchema. pre e novamente
‫ponto save e, em seguida, uma função com next.
‫Novamente, esta função aqui será
‫executada logo antes de um novo documento ser realmente salvo.
‫E, portanto, é
‫o lugar perfeito para realmente especificar essa propriedade.
‫E eu poderia, é claro, ter feito isso em um
‫controlador bem próximo a este código, por exemplo.
‫Mas eu realmente quero que isso aconteça, meio que automaticamente.
‫Então, meio que nos bastidores.
‫Porque mais tarde teremos outro local
‫onde atualizaremos a senha e depois faríamos questão de incluir
‫o mesmo código lá.
‫E assim, de novo, acontece,
‫meio que, nos
‫bastidores, sem que tenhamos que nos preocupar com isso.
‫Agora, quando exatamente queremos
‫definir a propriedade passwordChangedAt como agora?
‫Bem, só queremos quando realmente modificamos a
‫propriedade da senha.
‫E não tenho certeza se usamos esse truque antes,
‫mas de qualquer maneira, vamos usá-lo agora.
‫Então, se não tivermos modificado, então, se não for isso. isModified, assim como este,
‫e então
‫o nome da propriedade, então a senha.
‫Nesse caso, volte imediatamente e execute
‫o próximo middleware.
‫Ok, não assim, mas assim.
‫Então, novamente, se não modificamos a propriedade
‫da
‫senha, bem, é claro, não manipule o passwordChangedAt.
‫Mas que tal criar um novo documento?
‫Bem, quando criamos um novo
‫documento, modificamos a
‫senha de fato e configuramos a propriedade passwordChangedAt, certo?
‫Bem, na implementação atual, realmente o faríamos.
‫Mas há algo mais que podemos usar aqui.
‫Então, basicamente, queremos sair dessa função de
‫middleware imediatamente, se a senha não tiver sido modificada
‫ou se o documento for novo, podemos usar a
‫propriedade isNew.
‫E, novamente, esta é uma daquelas coisas muito boas que
‫você aprende lendo sua documentação.
‫E assim, não posso enfatizar o suficiente o quão importante é realmente
‫ler as documentações quando você precisa de algo que não
‫consegue encontrar em lugar nenhum.
‫Porque, realmente há tantas coisas
‫lá que são completamente impossíveis de ensinar em um curso.
‫De qualquer forma, se o código passar nessa verificação
‫aqui, bem, então vamos simplesmente
‫dizer isso. passwordChangedAt = Data. agora.
‫E então, ligamos em seguida.
‫Bem, em teoria, isso deveria funcionar bem, mas,
‫na verdade, na
‫prática, às vezes acontece um pequeno problema.
‫E esse problema é que às vezes salvar no banco de
‫dados é um pouco mais lento do que emitir o JSON Web
‫Token, fazendo com que o timestamp da senha alterada
‫às vezes seja definido um pouco
‫depois que o JSON Web Token foi criado.
‫E isso fará com que o
‫usuário não consiga fazer login usando o novo token.
‫Porque, lembre-se, todo o motivo pelo qual esse carimbo de data /
‫hora aqui realmente existe é para que possamos compará-lo
‫com o carimbo de data / hora no JSON Web Token, certo?
‫Então, só para lembrar, é,
‫bem, bem aqui, onde verificamos se o usuário alterou a
‫senha depois que o token foi emitido.
‫E então, aqui embaixo, onde criamos este novo token
‫na senha de redefinição.
‫Então, bem aqui, lembre-se, criamos esse novo token
‫e, novamente, às vezes acontece que esse token é criado
‫um pouco antes de o carimbo de data / hora da
‫senha alterada ter sido realmente criado.
‫E então, só precisamos corrigir isso subtraindo um segundo.
‫Então, basicamente, mil milissegundos.
‫E então isso vai colocar a passwordChangedAt um segundo no
‫passado, ok, que então é claro, não
‫será 100% precisa, mas isso não é problema nenhum,
‫porque um segundo aqui não faz diferença nenhuma.
‫É um pequeno hack, mas, novamente, não é problema.
‫Portanto, colocar esse passwordChanged um segundo no passado, garantirá
‫que o token seja sempre criado
‫após a alteração da senha.
‫Então, isso funciona agora, mas
‫como sempre, também vamos testá-lo rapidamente.
‫Ok, então de volta ao Postman.
‫Vamos fazer uma nova redefinição de senha ou, na
‫verdade, não era isso que eu
‫queria, mas é ótimo ver que o
‫código está realmente funcionando.
‫Portanto, o token é inválido ou expirou, e isso
‫porque, bem, 10 minutos se
‫passaram desde que realmente criei esse token.
‫E acho que ainda não tínhamos
‫testado isso, então é ótimo que agora, acidentalmente, realmente tenha feito.
‫Então, novamente, isso vem, caso você esteja se perguntando o que
‫diabos aconteceu, então
‫essa é, obviamente, esta mensagem de erro aqui.
‫E isso significa que não encontrou nenhum usuário
‫que tenha este token ou que tenha um token com mais
‫de 10 minutos no passado.
‫E então, de fato o que eu queria fazer, era esquecer a senha.
‫Então, vamos esperar.
‫Portanto, 8. 6 segundos, mas
‫isso pode ser por causa da minha conexão com a internet.
‫Portanto, se você executar isso em
‫um servidor, provavelmente será muito mais rápido.
‫Vamos pegar isso aqui, de volta ao Postman, e agora
‫redefinimos a senha, novamente com essa senha, realmente não importa se
‫é a mesma que a antiga.
‫E agora temos nosso sucesso aqui.
‫E agora de volta ao Compass.
‫Vamos recarregar e, de fato, obteremos passwordChangedAt.
‫E isso é realmente agora.
‫E então, se agora tentássemos realmente usar esse token, por exemplo,
‫para acessar essa rota protegida, bem, então isso
‫deve funcionar por causa daquela
‫pequena tag de um segundo que fizemos.
‫Então, funcionou e assim, na verdade, agora concluímos nossa
‫funcionalidade de redefinição de senha.
‫Então isso foi um pouco de código, mas é claro,
‫vale totalmente a pena.
‫Portanto, você deve sempre oferecer
‫esta funcionalidade em sua aplicação web,
‫pois caso contrário, um usuário que esquece sua senha fica completamente
‫enroscado, não pode mais usar sua
‫aplicação e, portanto, isso é uma prática terrível.
‫De qualquer forma, isso
‫termina, já, a parte de autenticação e autorização desta seção.
‫Então, novamente, está bem completo
‫e eu me diverti muito implementando isso.
‫Portanto, esta parte para mim é onde os aplicativos da
‫web realmente começam a ganhar vida.
‫Eu sei que não é realmente visível neste ponto,
‫com todos esses, apenas tokens e tokens de cópia
‫e colá-los em outro lugar.
‫Essa não é a ideia usual que temos de fazer
‫login, eu sei, mas é claro de novo,
‫um pouco mais tarde, quando finalmente começarmos a construir
‫o site dinâmico,
‫então, é claro, continuaremos usando essa autenticação
‫que acabamos de construir e então também se tornará visual nesse site dinâmico.
‫Em seguida, implementaremos a funcionalidade de atualização
‫e exclusão do usuário
‫e, em seguida, falaremos também sobre segurança.
‫Isso é o que vem pela frente para o resto
‫da seção, então certifique-se de não perder isso.