Pesquisa de site

Como lidar com o fluxo de trabalho de esquecimento e redefinição de senha usando React e Node.js


Não deixe os visitantes do seu site esperando – deixe-os redefinir a senha caso a tenham esquecido.

Os sistemas de autenticação desempenham um papel crucial no fornecimento de uma experiência de usuário segura e contínua. Um fluxo de trabalho de autenticação normalmente envolve dois processos: inscrição e login.

À medida que o número de serviços online aumenta, as pessoas criam contas, e cada conta requer credenciais de login exclusivas. No entanto, isso torna mais fácil esquecer ou confundir as credenciais de login. Para resolver isso, seu aplicativo deve implementar um recurso de redefinição de senha que permita ao usuário redefinir sua senha de maneira conveniente e segura.

Configure o projeto React

Você pode implementar um fluxo de trabalho de redefinição de senha de várias maneiras – não existe um padrão universal que todo aplicativo deva seguir. Em vez disso, você deve adaptar a abordagem escolhida para atender às necessidades específicas da sua aplicação.

O fluxo de trabalho que você aprenderá aqui inclui as seguintes etapas:

Para começar, inicialize rapidamente um projeto React. Em seguida, instale o Axios, uma biblioteca de solicitação HTTP JavaScript.

npm install axios

Você pode encontrar o código do projeto neste repositório GitHub.

Crie um componente de login

No diretório src, crie um novo arquivo components/Login.js e adicione o código a seguir. Comece definindo o processo de redefinição de senha:

import axios from "axios";
import React, { useState } from "react";
import { useContext } from "react";
import { RecoveryContext } from "../App";
import "./global.component.css";
export default function Login() {
  const { setPage, setOTP, setEmail } = useContext(RecoveryContext);
  const [userEmail, setUserEmail] = useState(""); 
  function sendOtp() {
    if (userEmail) {
      axios.get(`http://localhost:5000/check_email?email=${userEmail}`).then((response) => {
        if (response.status === 200) {
          const OTP = Math.floor(Math.random() * 9000 + 1000);
          console.log(OTP);
          setOTP(OTP);
          setEmail(userEmail);
          axios.post("http://localhost:5000/send_email", {
            OTP,
            recipient_email: userEmail,
          })
          .then(() => setPage("otp"))
          .catch(console.log);
        } else {
          alert("User with this email does not exist!");
          console.log(response.data.message);
        }}).catch(console.log);
    } else {
      alert("Please enter your email");
    }}

Este código cria uma função que envia uma senha de uso único (OTP) para o endereço de e-mail de um usuário. Ele primeiro verifica o usuário verificando seu e-mail no banco de dados antes de gerar e enviar o OTP. Finalmente, ele atualiza a UI com a página OTP.

Conclua o componente de login adicionando código para renderizar o elemento de formulário JSX de login:

  return (
    <div>
      <h2>Login</h2>
      <form>
        <label /> Email: 
        <input type="email" value={userEmail} onChange={(e) => { setUserEmail(e.target.value) }} />
        <label /> Password:
        <input type="password" />
        <button type="submit">login</button>
      </form>
      <a href="#" onClick={() => sendOtp()}>
        Forgot Password
      </a>
    </div>
  );
}

Crie um componente de verificação OTP

Para garantir a validade de um código inserido por um usuário, você precisa compará-lo com o código enviado para seu e-mail.

Crie um novo arquivo components/OTPInput.js e adicione este código:

import React, { useState, useContext, useEffect } from "react";
import { RecoveryContext } from "../App";
import axios from "axios";
import "./global.component.css";
export default function OTPInput() {
  const { email, otp, setPage } = useContext(RecoveryContext);
  const [OTPinput, setOTPinput] = useState( "");
  function verifyOTP() {
    if (parseInt(OTPinput) === otp) {
      setPage("reset");
    } else {
      alert("The code you have entered is not correct, try again re-send the link");
    }
  }

O código cria um componente React onde os usuários verificam seu código OTP. Verifica se o código inserido corresponde ao armazenado no objeto de contexto. Se for válido, exibe a página de redefinição de senha. Por outro lado, mostra um alerta solicitando ao usuário que tente novamente ou reenvie o OTP.

Você pode verificar o código neste repositório que implementa uma função para reenvio de OTPs e um temporizador de expiração para o código OTP.

Finalmente, renderize os elementos JSX de entrada.

  return (
    <div>
      <h3>Email Verification</h3>
      <p>We have sent a verification code to your email.</p>
      <form>
         <input type="text" value={OTPinput} onChange={(e) => { setOTPinput(e.target.value) }} /> 
          <button onClick={() => verifyOTP()}>Verify Account</button> 
          <a onClick={() => resendOTP()} > Didn't receive code? 
            {disable ? `Resend OTP in ${timerCount}s` : " Resend OTP"}
          </a> 
      </form>
    </div>
  );}

Crie o componente de redefinição de senha

Crie um novo arquivo components/Reset.js e adicione este código:

import React, {useState, useContext} from "react";
import { RecoveryContext } from "../App";
import axios from "axios";
import "./global.component.css";
export default function Reset() {
  const [password, setPassword] = useState("");
  const { setPage, email } = useContext(RecoveryContext);
  function changePassword() {
    if (password) {
      try {
        axios.put("http://localhost:5000/update-password", {
          email:email,
          newPassword: password,
        }).then(() => setPage("login"));
        return alert("Password changed successfully, please login!");
      } catch (error) {console.log(error);}}
    return alert("Please enter your new Password");
 }
  return (
    <div>
      <h2> Change Password </h2>
        <form>
          <label /> New Password: 
          <input 
            type="password"
            placeholder="........"
            required=""
            value={password}
            onChange={(e) => setPassword(e.target.value)} />
          <button onClick={() => changePassword()}>Reset passwod </button>
        </form>
    </div>
  );
}

Este código renderiza um formulário que permite aos usuários inserir uma nova senha. Quando o usuário clicar em enviar, será enviada uma solicitação ao servidor para atualizar sua senha no banco de dados. Em seguida, ele atualizará a IU se a solicitação for bem-sucedida.

Atualize seu componente App.js

Faça as alterações abaixo em seu arquivo src/App.js:

import { useState, createContext } from "react";
import Login from "./components/Login";
import OTPInput from "./components/OTPInput";
import Reset from "./components/Reset";
import "./App.css";
export const RecoveryContext = createContext();
export default function App() {
  const [page, setPage] = useState("login");
  const [email, setEmail] = useState("");
  const [otp, setOTP] = useState("");
  function NavigateComponents() {
    if (page === "login") return <Login />;
    if (page === "otp") return <OTPInput />;
    if (page === "reset") return <Reset />;
  }
  return (
    <div className="App-header"> 
      <RecoveryContext.Provider
        value={{ page, setPage, otp, setOTP, email, setEmail }}>
        <div>
          <NavigateComponents />
        </div>
      </RecoveryContext.Provider>
    </div>
  );
}

Esse código define um objeto de contexto que gerencia o estado do aplicativo, que inclui o e-mail do usuário, o código OTP e as diversas páginas do aplicativo. Essencialmente, o objeto de contexto torna possível passar os estados necessários entre diferentes componentes – uma alternativa ao uso de adereços.

Ele também inclui uma função que lida com a navegação da página com facilidade, sem a necessidade de renderizar novamente componentes inteiros.

Configurar um servidor Express.js

Com a configuração do cliente, configure um serviço de autenticação de back-end para lidar com a funcionalidade de redefinição de senha.

Para começar, crie um servidor web Express e instale estes pacotes:

npm install cors dotenv nodemailer mongoose

Em seguida, crie um banco de dados MongoDB ou configure um cluster MongoDB na nuvem. Em seguida, copie a cadeia de conexão fornecida, crie um arquivo ENV no diretório raiz e cole a cadeia de conexão.

Para finalizar, você precisa configurar a conexão com o banco de dados e definir os modelos de dados para os dados do seu usuário. Use o código neste repositório para configurar a conexão com o banco de dados e definir os modelos de dados.

Defina as rotas da API

Idealmente, um serviço de back-end possui várias rotas que lidam com as solicitações HTTP dos clientes. Nesse caso, você precisará definir três rotas que gerenciarão as solicitações de API de envio de email, verificação de email e atualização de senha do cliente React.

Crie um novo arquivo chamado userRoutes.js no diretório raiz e adicione o seguinte código:

const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/userControllers');
router.get('/check_email', userControllers.checkEmail);
router.put('/update-password', userControllers.updatePassword);
router.post('/send_email', userControllers.sendEmail);
module.exports = router;

Controladores para as rotas da API

Os controladores são responsáveis por processar as solicitações HTTP dos clientes. Depois que um cliente faz uma solicitação para uma rota de API específica, uma função do controlador é invocada e executada para processar a solicitação e retornar uma resposta apropriada.

Crie um novo arquivo controllers/userControllers.js e adicione o código abaixo.

Use o código neste repositório para definir controladores para verificação de e-mail e rotas de API de atualização de senha.

Comece definindo o controlador de envio de email:

exports.sendEmail = (req, res) => {
  const transporter = nodemailer.createTransport({
    service: 'gmail',
    secure: true,
    auth: {
      user: process.env.MY_EMAIL,
      pass: process.env.APP_PASSWORD,
    },
  });
  const { recipient_email, OTP } = req.body;
  const mailOptions = {
    from: process.env.MY_EMAIL,
    to: recipient_email,
    subject: 'PASSWORD RESET',
    html: `<html>
             <body>
               <h2>Password Recovery</h2>
               <p>Use this OTP to reset your password. OTP is valid for 1 minute</p>
               <h3>${OTP}</h3>
             </body>
           </html>`,
  };
  transporter.sendMail(mailOptions, (error, info) => {
    if (error) {
      console.log(error);
      res.status(500).send({ message: "An error occurred while sending the email" });
    } else {
      console.log('Email sent: ' + info.response);
      res.status(200).send({ message: "Email sent successfully" });
    }
  });
};

Este código define uma função que usa Nodemailer para enviar um email com redefinição de OTP para um destinatário especificado. Ele configura um transportador usando sua própria conta e senha do Gmail.

Para obter a senha do aplicativo Gmail, você precisa gerar uma senha de aplicativo nas configurações da sua conta do Google. Em seguida, você usará essa senha no lugar da senha normal do Gmail para autenticar o Nodemailer.

Configurar o ponto de entrada do servidor

Crie um arquivo server.js no diretório raiz e adicione este código:

const express = require('express');
const cors = require('cors');
const app = express();
const port = 5000;
require('dotenv').config();
const nodemailer = require('nodemailer');
const connectDB = require('./utils/dbconfig');
connectDB();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);
app.listen(port, () => {
  console.log(`Server is listening at http://localhost:${port}`);
});

Com o cliente e o servidor configurados, você pode executar os servidores de desenvolvimento para testar a funcionalidade da senha.

Construindo um serviço personalizado de redefinição de senha

Criar um sistema de redefinição de senha adaptando-o ao seu aplicativo e aos seus usuários é a melhor abordagem, embora existam soluções pagas e pré-construídas. Ao projetar esse recurso, você deve levar em consideração a experiência do usuário e a segurança, pois os ataques são uma ameaça constante.

Artigos relacionados: