No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Generando links de recuperaci贸n

17/20
Recursos

Aportes 7

Preguntas 3

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Se reacomoda un poco el servicio auth.service.js haciendo una separaci贸n de responsabilidades, se crea el m茅todo sendRecovery el cual contiene la l贸gica para generar un link para recuperar la contrase帽a y el m茅todo sendMail contiene la configuraci贸n para poder enviar un email (transporter).

Lo que hace es:

  1. Validar si el email se encuentra en la base de datos, si todo bien, se obtiene el user.

  2. Se genera un payload con el user.id.

  3. Se genera un token que expira en 15 minutos, incluye el payload con el id del usuario que solicita recuperar su contrase帽a. Por seguridad, el token debe guardarse en la BD y comprobarlo para evitar que env铆en un token indeseado.

  4. Se genera un link que incluye el token necesario para recuperar la contrase帽a. Desde el frontend debe haber una vista para ello.

  5. Se llama el m茅todo update de UserService para actualizar los datos del usuario asignandole el token generado.

  6. Se establece el cuerpo del email.

  7. Se ejecuta el m茅todo sendMail que recibe el cuerpo del email.

const boom = require('@hapi/boom');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const nodemailer = require('nodemailer');

const { config } = require('../config/config');

const UserService = require('./user.service');
const service = new UserService();

class AuthService {
  async getUser(email, password) {
    const user = await service.findByEmail(email);
    if (!user) {
      throw boom.unauthorized();
    }

    const isMatch = await bcrypt.compare(password, user.password);
    if (!isMatch) {
      throw boom.unauthorized();
    }
    delete user.dataValues.password;
    return user;
  }

  signToken(user) {
    const payload = {
      sub: user.id,
      role: user.role,
    };
    const token = jwt.sign(payload, config.jwtSecret);
    return {
      user,
      token,
    };
  }

  async sendRecovery(email) {
    const user = await service.findByEmail(email);
    if (!user) {
      throw boom.unauthorized();
    }

    const payload = { sub: user.id };
    const token = jwt.sign(payload, config.jwtRecoverySecret, {
      expiresIn: '15min',
    });
    const link = `https://myfrontend.com/recovery?token=${token}`;
    await service.update(user.id, { recoveryToken: token });
    const mail = {
      from: `"Foo Boo 馃懟" <${config.mailerEmail}>`,
      to: `${user.email}`,
      subject: 'Email para recuperar contrase帽a',
      html: `<b>Ingresa a este link para recuperar tu contrase帽a: ${link}</b>`,
    };

    const rta = await this.sendMail(mail);
    return rta;
  }

  async sendMail(infoMail) {
    const transporter = nodemailer.createTransport({
      host: 'smtp.gmail.com',
      secure: true, // true for 465, false for other ports
      port: 465,
      auth: {
        user: config.mailerEmail,
        pass: config.mailerPassword,
      },
    });

    await transporter.sendMail(infoMail);
    return { message: 'Mail sent' };
  }
}

module.exports = AuthService;

En el endpoint /recovery de auth.router.js se hace el cambio del m茅todo sendMail por sendRecovery:

router.post('/recovery', async (req, res, next) => {
  try {
    const { email } = req.body;
    const rta = await service.sendRecovery(email);
    res.json(rta);
  } catch (error) {
    next(error);
  }
});

Debido a que se necesita ingresar un nuevo campo, se requiere generar una nueva migraci贸n (recovery-token-field), en el boilerplate de dicha migraci贸n se agrega una nueva columna a USER_TABLE. Esto contiene el nombre de la tabla, el nombre del nuevo campo y los tipos de datos, as铆 como un m茅todo para hacer rollback (removeColumn):

'use strict';

const { USER_TABLE } = require('../models/user.model');

module.exports = {
  up: async (queryInterface, Sequelize) => {
    await queryInterface.addColumn(USER_TABLE, 'recovery_token', {
      field: 'recovery_token',
      allowNull: true,
      type: Sequelize.DataTypes.STRING,
    });
  },

  down: async (queryInterface) => {
    await queryInterface.removeColumn(USER_TABLE, 'recovery_token');
  },
};

Tambi茅n es necesario agregar un nuevo campo al modelo user.model.js:

recoveryToken: {
    field: 'recovery_token',
    allowNull: true,
    type: DataTypes.STRING,
  },

Despu茅s de tener el boilerplate listo y haber modificado el modelo de usuario, se corre la migraci贸n con npm run migrations:run.

Definitivamente cualquier persona que quiera ser full stack debe de tomar este maravilloso curso. Pa鈥 lante compa帽eros el camino es largo pero la recompensa es jugosa. 馃榿馃捇馃崡

Yo hice un MailService para tener la logica de los correos ahi

const nodemailer = require('nodemailer');
const config = require('../config');
const { host, port, user, password } = config.smtp;

class MailService {
  constructor() {
    this.transporter = nodemailer.createTransport({
      host,
      port,
      secure: true,
      auth: {
        user,
        pass: password,
      },
    });
  }

  async sendEmail(emailData) {
    await this.transporter.sendMail({
      from: emailData.from,
      to: emailData.to,
      subject: emailData.subject,
      text: emailData.text,
      html: emailData.html,
    });
  }
}
module.exports = MailService;


si les da un error como este.

{
  "message": "self signed certificate in certificate chain",
  "stack": "Error: self signed certificate in certificate chain\n    at TLSSocket.onConnectSecure (node:_tls_wrap:1535:34)\n    at TLSSocket.emit (node:events:513:28)\n    at TLSSocket._finishInit (node:_tls_wrap:949:8)\n    at TLSWrap.ssl.onhandshakedone (node:_tls_wrap:730:12)"
}```

deben poner el siguiente c贸digo asi


tls: {
rejectUnauthorized: false
}



nodemailer.createTransport({
host: process.env.MAIL_SERVER,
secure: false,
port: 587,
auth: {
user: process.env.MAIL_USERNAME,
pass: process.env.MAIL_PASSWORD
},
tls: {
rejectUnauthorized: false
}
}

Con eso se me arreglo.


As铆 configure la constante con la informaci贸n del correo:

const mail = {
      from: config.emailUser,
      to: `${user.email}`,
      subject: "ApiFakeStore: Recupera tu cuenta 馃棟锔",
      html: `
        <b>Ingresa a este link 馃憞馃憞 para recuperar tu cuenta 馃棟锔</b>
        <br>
        <a href="${link}" target="_blank">Recuperar cuenta</a>
      `,
    }

No me di cuenta en que momento del video cambiamos el router para agregarle el sendRecovery. Excelente clase.

estas clases son geniales