InsidersByte/honeymoon-gift-list

View on GitHub
server/routes/api/authenticate.js

Summary

Maintainability
A
3 hrs
Test Coverage
const uuid = require('uuid');
const jwt = require('jsonwebtoken');
const User = require('../../models/User');
const wrap = require('../../utilities/wrap');
const Mailer = require('../../mail');
const { ONE_DAY_S, ONE_DAY_MS } = require('../../constants');
const { MINIMUM_PASSWORD_LENGTH, MINIMUM_PASSWORD_MESSAGE } = require('../../constants/user');

const mailer = new Mailer();

module.exports = ({ express, config }) => {
  const router = new express.Router();

  router.post(
    '/',
    wrap(function* authenticate(req, res) {
      req.checkBody('email').isEmail();
      req.checkBody('password').notEmpty();

      const errors = req.validationErrors();

      if (errors) {
        return res.status(400).send(errors);
      }

      const { email, password } = req.body;

      const user = yield User.forge({ email }).fetch();

      if (!user) {
        return res.status(400).json({ message: 'Authentication failed.' });
      }

      const validPassword = user.comparePassword(password);

      if (!validPassword) {
        return res.status(400).json({ message: 'Authentication failed.' });
      }

      const token = jwt.sign(
        {
          name: user.get('name'),
          email: user.get('email'),
        },
        config.secret,
        {
          expiresIn: ONE_DAY_S,
        }
      );

      return res.json({ token });
    })
  );

  router.route('/resetPassword').post(
    wrap(function* resetPassword(req, res) {
      req.checkBody('email').isEmail();

      const errors = req.validationErrors();

      if (errors) {
        return res.status(400).send(errors);
      }

      const { email } = req.body;

      const user = yield User.forge({ email }).fetch();

      if (!user) {
        // Send 200 with reset password so that people can't guess the email address
        return res.json({ message: `A email has been sent to ${email} with further instructions.` });
      }

      const resetPasswordToken = uuid.v4();

      user.set({
        resetPasswordToken,
        resetPasswordExpires: Date.now() + ONE_DAY_MS,
      });

      yield user.save();

      yield mailer.send(
        {
          to: email,
          subject: 'Reset Password',
          resetUrl: `http://${req.headers.host}/admin/reset/${resetPasswordToken}`,
        },
        'resetPassword'
      );

      return res.json({ message: `A email has been sent to ${email} with further instructions.` });
    })
  );

  router.route('/resetPassword/:token').put(
    wrap(function* resetPassword(req, res) {
      req.checkBody('token').equals(req.params.token);
      req.checkBody('password', MINIMUM_PASSWORD_MESSAGE).isLength({ min: MINIMUM_PASSWORD_LENGTH });
      req.checkBody('confirmPassword').equals(req.body.confirmPassword);

      const errors = req.validationErrors();

      if (errors) {
        return res.status(400).send(errors);
      }

      const { token, password } = req.body;

      const user = yield User.forge({ resetPasswordToken: token }).where('reset_password_expires', '>', Date.now()).fetch();

      if (!user) {
        return res.status(400).json({ message: 'Password reset token is invalid or has expired.' });
      }

      user.set({
        resetPasswordToken: null,
        resetPasswordExpires: null,
        password,
      });

      yield user.save();

      yield mailer.send(
        {
          to: user.get('email'),
          subject: 'Your password has been changed',
        },
        'resetPasswordConfirmation'
      );

      return res.json({ message: 'Password Reset Successfully!' });
    })
  );

  return router;
};