Гид по технологиям

Сброс пароля в React и Express — пример и рекомендации

6 min read Authentication Обновлено 02 Jan 2026
Сброс пароля в React и Express — пример и рекомендации
Сброс пароля в React и Express — пример и рекомендации

Ноутбук на рабочем столе с открытым редактором кода.

Аутентификация — ключевая часть UX и безопасности. Процесс сброса пароля обычно состоит из двух базовых шагов: подтверждение владения почтой (или другим каналом) и замена пароля. В этом материале мы пройдём реальную реализацию на React и Express, обсудим слабые места и предложим улучшения.

Короткая схема процесса

Схема процесса сброса пароля

Простой рабочий флоу, который мы реализуем:

  • Клиент вводит email и запрашивает OTP.
  • Сервер проверяет, есть ли пользователь с этим email.
  • Сервер генерирует OTP и отправляет письмо (Nodemailer).
  • Клиент вводит OTP и верифицирует код.
  • После успешной верификации клиент отправляет новый пароль.
  • Сервер обновляет хеш пароля в базе.

Установка и подготовка React‑проекта

Начните с быстрой инициализации React‑проекта. Затем установите Axios:

npm install axios

Код проекта базируется на простом контексте RecoveryContext, чтобы не прокидывать пропсы через компоненты.

Важно: в продакшне используйте HTTPS и настройте CORS корректно. Не храните секреты в коде.

Компонент Login

Ниже — пример компонента для отправки запроса на генерацию и отправку OTP. Код проверяет наличие email в базе, генерирует 4‑значный OTP и вызывает API отправки письма.

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

Далее — JSX формы входа. Обратите внимание: кнопка “Forgot Password” запускает sendOtp.

  return (
    

Login

); }

Примечание: в реальном приложении форма логина должна предотвращать стандартную отправку формы (e.preventDefault()) и выполнять аутентификацию через API.

Компонент ввода OTP

Компонент, который принимает код от пользователя и сравнивает его со значением из контекста. Если совпадает — переводит на страницу сброса пароля.

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

В интерфейсе полезно добавить кнопку повторной отправки (Resend) и таймер истечения валидности OTP. В примере репозитория есть реализация повторной отправки и таймера.

  return (
    

Email Verification

We have sent a verification code to your email.

{ setOTPinput(e.target.value) }} /> resendOTP()} > Didn't receive code? {disable ? `Resend OTP in ${timerCount}s` : " Resend OTP"}
);}

Важно: не позволяйте многократные попытки без задержки — применяйте rate limiting.

Компонент сброса пароля

Форма для ввода нового пароля и отправки запроса на сервер для обновления.

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 (
    

Change Password

); }

Советы по паролям: применяйте проверку на сложность (минимум символов, класс символов), хешируйте пароли на сервере (bcrypt/argon2), и не передавайте пароли в логах.

Обновление App.js

Контекст RecoveryContext позволяет хранить page/email/otp и переключать отображаемые компоненты.

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 ;
    if (page === "otp") return ;
    if (page === "reset") return ;
  }

  return (
    
); }

Контекст — удобный способ шарить состояния между компонентами, но для крупных приложений рассмотрите менеджеры состояния (Redux, Zustand) или route‑based навигацию.

Настройка Express.js сервера

Установите зависимости:

npm install cors dotenv nodemailer mongoose

Создайте базу MongoDB (локально или в облаке). Поместите строку подключения в файл .env. Конфигурация подключения и модели пользователя находятся в репозитории примера.

Определение маршрутов API

Файл routes/userRoutes.js:

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;

Контроллер отправки email

Пример контроллера использует Nodemailer и Gmail app password:

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: `
              
               

Password Recovery

Use this OTP to reset your password. OTP is valid for 1 minute

${OTP}

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

Важно: никогда не храните реальные пароли почты в репозитории. Для Gmail используйте App Passwords или сервисные учётные записи. Рассмотрите транзакционные почтовые сервисы (SendGrid, Mailgun) для повышения надёжности.

Точка входа сервера

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

Рекомендации по безопасности

Important: базовая реализация подходит для развития и тестирования, но для продакшна потребуется усиление защиты.

  • Хешируйте пароли на сервере: bcrypt или argon2. Никогда не храните пароли в открытом виде.
  • Ограничьте время жизни OTP (например, 5–15 минут) и храните время истечения на сервере.
  • Ограничьте число попыток ввода OTP и применяйте экспоненциальные задержки.
  • Используйте одноразовые токены на сервере, а не доверяйте только клиентскому состоянию.
  • Передавайте данные по HTTPS, на сервере включите строгую CORS‑политику.
  • Логируйте события безопасности (без секретов) и настраивайте оповещения о подозрительных попытках.
  • Для отправки email используйте проверенные SMTP‑поставщики с поддержкой DKIM/SPF.

Конфиденциальность и соответствие требованиям (GDPR)

  • Собирайте минимум данных: для сброса нужен только email.
  • Обрабатывайте запросы на удаление/экспорт данных, если это требует локальное законодательство.
  • Для EU‑пользователей храните данные в зоне с подходящими юридическими гарантиями или используйте контрактные механизмы переноса данных.
  • В уведомлениях о сбросе не включайте лишней личной информации.

Когда этот подход не подходит

  • Если у приложения повышенные требования безопасности (банкинг, медицина) — замените OTP на многофакторную аутентификацию (MFA) и/или аппаратные ключи.
  • Если у пользователя нет доступа к email — добавьте альтернативные каналы (SMS, телефон, аппаратные токены).
  • Если необходим сложный аудит — интегрируйте централизованный сервис аутентификации (Auth0, Okta) с логированием и SSO.

Альтернативные подходы

  • Токены восстановления по ссылке: вместо OTP сервер генерирует одноразовую ссылку с криптографически случайным токеном, отправляет на email и валидирует по запросу.
  • OAuth / SSO: используйте сторонних провайдеров (Google, Apple) и делегируйте управление паролями.
  • Passwordless: вход по magic link (ссылке без пароля).

Каждый подход имеет компромиссы: OTP проще, но менее устойчив к перехвату; ссылочный токен удобнее и безопаснее при корректной реализации.

Ментальные модели и эвристики

  • Принцип минимальных полномочий: давайте пользователю ровно то, что нужно для восстановления, ничего лишнего.
  • Доверяй, но проверяй: не полагайтесь на клиентские проверки — всё нужно проверять на сервере.
  • Defense in depth: сочетайте несколько мер (шифрование, таймауты, rate limit).

Факт‑бокс: ключевые числа и причины

  • Длина OTP: 4–8 цифр (4 — удобнее, 6+ — безопаснее).
  • Время жизни OTP: 60–900 секунд (1–15 минут) — баланс UX и безопасности.
  • Попытки ввода: 3–10 с постепенным блокированием.
  • Хеширование пароля: bcrypt/argon2 с достаточной cost‑параметром.

Критерии приёмки

  • Пользователь может запросить OTP по email и получить подтверждение от API (200).
  • OTP действительно приходит на почту и валиден в течение установленного времени.
  • После верификации OTP пользователь может задать новый пароль и успешно войти.
  • Пароль на сервере хранится в хеше, а не в открытом виде.
  • Логи не содержат паролей или OTP в открытом виде.

Чек‑лист для релиза (роль‑ориентированный)

  • Разработчик:
    • Проверил хеширование паролей.
    • Добавил rate limiting для эндпоинтов reset.
    • Настроил обработку ошибок и корректные HTTP статусы.
  • DevOps:
    • Настроил SSL/HTTPS и защиту от MITM.
    • Хранилище секретов настроено (vault, secrets manager).
  • Product/PM:
    • Убедился, что UX понятен: сообщения о безопасности, ожидании и ошибках.
  • QA:
    • Протестировал сценарии успешного и неуспешного сброса, повторную отправку и истечение срока действия.

Тесты и приёмочные сценарии

  • Сценарий успеха: существующий email → получение OTP → ввод корректного кода → смена пароля → вход с новым паролем.
  • Неверный email: пользователь получает понятное сообщение без утечки информации о существовании учётной записи.
  • Неверный OTP: система отвергает и учитывает попытку; после N попыток — временный блок.
  • Истёкший OTP: сервер возвращает детальное, но не компрометирующее сообщение.

Шаблон .env (совет)

  • DO NOT commit .env в VCS.
MONGO_URI=your_mongo_connection_string
MY_EMAIL=your_email@example.com
APP_PASSWORD=your_app_password
PORT=5000

Заключение

Сброс пароля — критичная точка взаимодействия с пользователем и возможная точка атаки. Простейшая реализация, как в этом примере, подойдет для быстрых прототипов и тестирования, но для продакшна требуется усиление: хеширование, rate limiting, безопасная отправка писем и учет соответствия конфиденциальности. Выбирайте модель (OTP, ссылочный токен, passwordless, SSO) исходя из требований безопасности и удобства пользователей.

Краткие выводы:

  • Реализуйте серверную проверку и хеширование паролей.
  • Ограничьте время и количество попыток для OTP.
  • Используйте проверенные SMTP‑поставщики и настройте DKIM/SPF.

Спасибо за внимание. Тестируйте сценарии, документируйте требования безопасности и регулярно пересматривайте политику восстановления пароля.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Ускорение macOS в VMWare на Windows
Виртуализация

Ускорение macOS в VMWare на Windows

Убрать тень под значками в Windows 10
Windows

Убрать тень под значками в Windows 10

Восстановить папку «Документы» в Windows
Windows

Восстановить папку «Документы» в Windows

Узнать серийный номер RAM в Windows
Hardware

Узнать серийный номер RAM в Windows

История загрузок и выключений Windows — как посмотреть
Windows

История загрузок и выключений Windows — как посмотреть

Ошибка 0x800700DF: как исправить в Windows
Windows

Ошибка 0x800700DF: как исправить в Windows