Source: controllers/authentications/forgotPassword.controller.js

import { resetTokens as resetTokensService, users as usersService } from '../../services';
import { logger, emailer } from '../../utils';

/**
 * Will destroy reset tokens that are past now.
 *
 * @memberof module:controllers/authentication
 * @param {Object} request - HTTP request object.
 * @param {Object} response - HTTP response object callback.
 * @param {Object} next - Next route callback handler.
 * @returns - Will return a http response body with a message confirming token emailed.
 * @returns - WIll return an error message if there is no request.body.
 * @returns - Will return an error message if no email provided.
 * @returns - Will return an error message if user cannot be found for the email address.
 * @returns - Will return an error message if previous reset tokens can not be updated as used for user requesting.
 * @returns - Will return an error message if a reset token cannot be generated.
 * @returns - Will return an error message if the reset token email cannot be sent.
 */
const forgotPassword = async (request, response, next) => {
  try {

    // Check we have a request body
    if (!request.body) {
      throw new Error('AUTHENTICATION ERROR: Forgot password request body is empty.');
    }

    // Parse request body
    const { email } = request.body;

    // Check we have an email
    if (!email) {
      throw new Error('AUTHENTICATION ERROR: Forgot password request has no user email.');
    }

    // Check email exists
    const user = await usersService.findOneByEmail(email);

    // Update all reset tokens for given id
    const isTokensSetUsed = await resetTokensService.updateUsedByUserId(user.id);

    if (!isTokensSetUsed) throw new Error('AUTHENTICATION ERROR: Reset tokens could not be set as used.');

    // Get reset token
    const createdResetToken = await resetTokensService.createOne(user.id);

    // Check we have a token
    if (!createdResetToken) {
      throw new Error('AUTHENTICATION ERROR: Forgot password reset token not generated.');
    }

    // Recipient details
    const name = user.firstName;
    const emailAddress = email;
    const token = createdResetToken.token;

    // Send email
    const emailResponse = await emailer.forgotEmail(name, emailAddress, token);

    // Check we have an email response
    if (!emailResponse) throw new Error('AUTHENTICATION ERROR: Forgot reset email could not be sent.');

    // API response
    const responseBody = response.status(201).json({
      status: 201,
      message: `SUCCESS: Reset token sent to <${email}>.`,
    });

    return responseBody;
  } catch (error) {

    // Log error message since we are not passing them on
    logger.debug(error.message);

    // Send consistent error message to limit attack vectors
    const responseBody = response.status(400).json({
      status: 400,
      message: `FORGOT PASSWORD ERROR: Reset token was not sent.`,
    });

    return responseBody;
  }
};

export { forgotPassword };
export default forgotPassword;