Рубрики
Без рубрики

Как сбросить пароль с помощью JWT и Node.Js

Как сбросить пароль с помощью JWT и Node.Js

Что такое сброс пароля?

Сброс пароля – это функция, которая помогает пользователям, которые по каким-либо причинам забыли свой пароль.

Чаще всего, чтобы сбросить пароль, пользователь просто нажимает на кнопку “забыл пароль”. Затем на адрес электронной почты пользователя будет отправлено письмо с подтверждением или токен сброса.

Это также можно сделать с помощью мобильного телефона.

В этой статье мы поговорим о том, как сбросить пароль JWT и Node.js.

Часто люди забывают свои пароли – это можно объяснить тем, что в мире Интернета повысилась безопасность, где используется несколько алгоритмов для повышения оптимальной безопасности. Это означает, что пароли должны быть надежными, а надежные пароли бывает трудно запомнить.

Кому это нужно?

На самом деле, это нужно двум категориям людей: Пользователи приложений и дизайнеры/разработчики приложений. Однако разработчики приложений должны понимать базовую структуру, которую необходимо создать для того, чтобы их приложения могли использовать эту функциональность.

В этой статье мы будем использовать существующее приложение, описанное в статье 5 шагов к аутентификации Node.js с помощью JWT. Репозиторий можно найти здесь, в ветке аутентификации.

Необходимые условия

  • Уже существующее приложение.
  • JWT-аутентификация.

Инструменты

Node и менеджеры пакетов

  • Postman
  • Bcrypts
  • JWT
  • Nodemailer-express-handlebars
  • Nodemailer

Шаги

  1. Маршрут для забытого пароля.
  2. Настройте nodemailer для отправки шаблонов электронной почты
  3. Создайте шаблон электронной почты
  4. Метод отправки подтверждения пароля
  5. Маршрут для подтверждения сброса пароля.
  6. Метод для генерации и отправки токена на электронную почту пользователя.

Прежде чем мы продолжим, клонируйте приложение с GitHub и перейдите в ветку аутентификации.

git clone https://github.com/generalgmt/RESTfulAPITutorial
git checkout authentication
npm install
git checkout -b password-reset

Суть приведенного выше кода заключается в том, чтобы дать нам копию кода в ветке аутентификации в новой ветке под названием “password-reset”.

Теперь, когда это сделано, мы находимся в новой ветке, где мы добавим функциональность в приложение с аутентификацией JWT

1. Маршрут для забытого пароля

В папке route откройте файл todoListRoutes.js.
Добавьте приведенный ниже фрагмент в файл:

app.route('/auth/forgot_password')
  .get(userHandlers.render_forgot_password_template)
  .post(userHandlers.forgot_password);

Маршрут get загружает шаблон, с которым пользователь должен взаимодействовать, и запрашивает сброс пароля.

Пост используется для отправки имени пользователя или адреса электронной почты, которые вы хотите сбросить.

Обработчик ‘forgot_password’ будет слушать пост-запрос на ‘/auth/forgot_password’.

2. Настройте nodemailer для отправки шаблонов электронных писем

Другими словами, чтобы отправлять электронные письма с помощью nodemailer, нам нужно его настроить.
Для удобства в этой статье мы будем использовать шаблонизатор nodemailer Handlebars.js.

var  hbs = require('nodemailer-express-handlebars'),
  email = process.env.MAILER_EMAIL_ID || 'auth_email_address@gmail.com',
  pass = process.env.MAILER_PASSWORD || 'auth_email_pass'
  nodemailer = require('nodemailer');

var smtpTransport = nodemailer.createTransport({
  service: process.env.MAILER_SERVICE_PROVIDER || 'Gmail',
  auth: {
    user: email,
    pass: pass
  }
});

var handlebarsOptions = {
  viewEngine: 'handlebars',
  viewPath: path.resolve('./api/templates/'),
  extName: '.html'
};

smtpTransport.use('compile', hbs(handlebarsOptions));

Как вы видите, я использовал метод createTransport в Nodemailer для добавления типа службы и параметров аутентификации. Ознакомьтесь с Nodemailer для получения дополнительной информации.

После этого в опции Nodemailer были добавлены движок шаблона и местоположение шаблона.

Это поможет Nodemailer узнать, какой движок использовать и где искать шаблоны.

Примечание: Если вы не используете Gmail, измените опцию сервиса в конфигурации nodemailer на имя вашего проовайдера.

3. Шаблоны электронных писем

Создайте папку под названием “templates” внутри папки api.
В этой папке создайте два HTML-файла под названием “forgot-password-email.html” и “reset-password-email.html” соответственно.

В файл “forgot-password-email” добавьте приведенный ниже фрагмент кода:

<!DOCTYPE html>
<html>
  <head>
    <title>Forget Password Email</title>
  </head>

  <body>
    <div>
      <h3>Dear {{name}},</h3>
      <p>You requested for a password reset, kindly use this <a href="{{url}}">link</a> to reset your password</p>
      <br>
      <p>Cheers!</p>
    </div>
  </body>
</html>

В файл “reset-password-email.html” вставьте приведенный ниже фрагмент кода.

<!DOCTYPE html>
<html>
  <head>
    <title>Password Reset</title>
  </head>

  <body>
    <div>
      <h3>Dear {{name}},</h3>
      <p>Your password has been successful reset, you can now login with your new password.</p>
      <br/>
      <p>Cheers!</p>
    </div>   
  </body>
</html>

4. Метод отправки подтверждения пароля (forgot_password)

Этот метод будет искать существование имени пользователя/электронной почты в базе данных. Если пользователь существует, на его электронную почту будет отправлена ссылка на сброс пароля с токеном.

Async waterfall помогает убедиться, что каждая из функций выполняется одна за другой (т.е. последовательно, что часто называют синхронностью).

Первая функция в последовательности выполняет поиск пользователя в базе данных, если он существует, генерируется токен и обновляется объект пользователя в базе данных. После этого пользователю отправляется электронное письмо с маркером, который он/она должен использовать для сброса пароля.

Это служит средством подтверждения того, что вы являетесь владельцем введенного имени пользователя/электронной почты.

exports.forgot_password = function(req, res) {
  async.waterfall([
    function(done) {
      User.findOne({
        email: req.body.email
      }).exec(function(err, user) {
        if (user) {
          done(err, user);
        } else {
          done('User not found.');
        }
      });
    },
    
    function(user, done) {
      // create the random token
      crypto.randomBytes(20, function(err, buffer) {
        var token = buffer.toString('hex');
        done(err, user, token);
      });
    },
    
    function(user, token, done) {
      User.findByIdAndUpdate({ _id: user._id }, { reset_password_token: token, reset_password_expires: Date.now() + 86400000 }, { upsert: true, new: true }).exec(function(err, new_user) {
        done(err, token, new_user);
      });
    },
    
    function(token, user, done) {
      var data = {
        to: user.email,
        from: email,
        template: 'forgot-password-email',
        subject: 'Password help has arrived!',
        context: {
          url: 'http://localhost:3000/auth/reset_password?token=' + token,
          name: user.fullName.split(' ')[0]
        }
      };

      smtpTransport.sendMail(data, function(err) {
        if (!err) {
          return res.json({ message: 'Kindly check your email for further instructions' });
        } else {
          return done(err);
        }
      });
    }
  ],
                  
  function(err) {
    return res.status(422).json({ message: err });
  });
};

5. Маршрут для подтверждения пароля и сброса

app.route('/auth/reset_password')
  .get(userHandlers.render_reset_password_template)
  .post(userHandlers.reset_password);

Когда пользователь, запросивший запрос пароля, нажимает на ссылку в отправленном ему письме, маршрут ‘/auth/reset_password’ отображает шаблон, с помощью которого пользователь может ввести и подтвердить новый пароль.

За этим следует AJAX-запрос к back-end на маршруте post для сохранения нового пароля.

Сниппет кода можно найти в folder/js.

6. Метод сохранения нового пароля.

Чтобы сохранить пароль, сначала необходимо выполнить несколько основных действий.

Проверить, существует ли токен в базе данных и не истек ли срок его действия.
Проверить, правильно ли введен пароль, сравнив два введенных пароля.

exports.reset_password = function(req, res, next) {
  User.findOne({
    reset_password_token: req.body.token,
    reset_password_expires: {
      $gt: Date.now()
    }
  }).exec(function(err, user) {
    if (!err && user) {
      if (req.body.newPassword === req.body.verifyPassword) {
        user.hash_password = bcrypt.hashSync(req.body.newPassword, 10);
        user.reset_password_token = undefined;
        user.reset_password_expires = undefined;
        user.save(function(err) {
          if (err) {
            return res.status(422).send({
              message: err
            });
          }
          else {
            var data = {
              to: user.email,
              from: email,
              template: 'reset-password-email',
              subject: 'Password Reset Confirmation',
              context: {
                name: user.fullName.split(' ')[0]
              }
            };

            smtpTransport.sendMail(data, function(err) {
              if (!err) {
                return res.json({ message: 'Password reset' });
              } else {
                return done(err);
              }
            });
          }
        });
      }
      else {
        return res.status(422).send({
          message: 'Passwords do not match'
        });
      }
    }
    else {
      return res.status(400).send({
        message: 'Password reset token is invalid or has expired.'
      });
    }
  });
};

Из приведенного выше фрагмента кода следует, что если токен существует в базе данных, срок его действия не истек, а два пароля совпадают, то новый пароль будет сохранен в базе данных.

После этого пользователю отправляется электронное письмо об успешном завершении сброса пароля.

Тестирование

Для тестирования данного руководства был добавлен скелетный код front-end. Его можно найти в общей папке. Полный код здесь на GitHub – пожалуйста, сделайте ответвление на password-reset.

После клонирования и установки пакетов выполните команду “npm run start”.

В браузере перейдите на “localhost:3000”

Если вы все сделали правильно, вы должны увидеть:

Снятый на экран 2017-07-29 в 9.26.13 am.png

и в браузере,

Снимок экрана 2017-07-29 в 9.31.05 AM.PNG

Нажмите на ссылку reset, и вы перейдете на новую страницу с формой для сброса пароля, как показано ниже:

Снимок экрана 2017-07-29 в 9.33.02 AM.PNG

Введите адрес электронной почты, по которому вы хотите сбросить пароль, и нажмите кнопку отправить.

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

Примечание: Не забудьте добавить учетные данные электронной почты в конфигурации nodemail.

Выстрел на экране 2017-07-29 на 10.32.08 am.png

Нажмите на ссылку в электронном письме. Ссылка должна открыть новую вкладку браузера, и вам будет предложено ввести новый пароль с подтверждением.

Снимок экрана 2017-07-29 в 9.33,33 am.png

После этого нажмите кнопку отправить. Если срок действия сгенерированного токена не истек, пароль электронной почты будет сброшен на пароль, и вы сможете войти в систему. 😃

Завершение

С помощью нескольких шагов, описанных выше, вы можете добавить функцию сброса пароля в существующее приложение Node.js.

Оригинал: “https://www.codementor.io/@olatundegaruba/password-reset-using-jwt-ag2pmlck0”