The Code in Users.js gets an error in the snippet at: qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {.

I've tried adding return statement to make sure I'm not sending the response multiple times. I can see that the data_url when converted to image online shows me a QR code but I'm unable to see that when I'm using Postman.

router.post(
  "/",
  [
    check("name", "Name is required")
      .not().isEmpty(),
    check("email", "Please include a valid email").isEmail(),
    check(
      "password",
      "Please enter a password with 6 or more characters"
    ).isLength({ min: 6 })
  ],
async (req, res) => {
    console.log("hi");
    console.log(JSON.stringify(req.body));
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      //  return res.status(400).json({ errors: errors.array() });
    }

    const {
      name,
      email,
      password,
      type_of_user,
      question1,
      answer1,
    question2,
    answer2
    } = req.body;

    try {
      let user = await User.findOne({ email }); // await User.findOne({ email });



      user = new User({
        name,
        email,
        avatar,
        password,
        type_of_user,
        question1,
        answer1,
        question2,
        answer2
      });

      const salt = await bcrypt.genSalt(10); //await

      user.password = await bcrypt.hash(password, salt); // await

      user
        .save()
        .then(result => {
          // MFAOptions & secret will generate a secret
          const MFAOptions = {
            issuer: "xyz",
            user: req.body.email,
            length: 64
          };
          const secret = speakEasy.generateSecret(MFAOptions);

          const token = jwt.sign(
            {
              name: user.name,
              email: user.email,
              twofactor: false
            },

            config.get("jwtSecret"), // chnaged from process env jwt
            {
              expiresIn: "1h"
            }
          );

          // update the user that is just created:
          user
            .update(
              { email: req.body.email },
              {
                $set: { twoFASecret: secret.base32 }
              }
            )
            .exec()
            .then(result => {
              console.log(result);
              qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
                console.log(data_url);
                res.status(200).json({
                  img: data_url,
                  token: token
                });
              });
              return;
            })
            //if anything wrong, throws an error
            .catch(err => {
              console.log(err);
              //  res.status(500).json({ error: err });
            });
        })
        // originaly this will end here, but now it should redirect to twoFA route,
        // if something wrong, shows an error
        .catch(err => {
          console.log(err);
          //  res.status(500).json({ error: err });
        });

      // user with an id, primise which returns an id

      const payload = {
        user: {
          id: user.id
        }
      };

      jwt.sign(
        payload,
        config.get("jwtSecret"),
        { expiresIn: 3600 }, 
        (err, token) => {
          if (err) throw err;
          res.json({ token });
        }
      );
      //  } //else end
    } catch (err) {
      console.error(err.message);
      res.status(500).send("Server error");
    }
  }
);



module.exports = router;
1

There are 1 best solutions below

0
On

I think your problem with executing this line qrcode.toDataURL(secret.otpauth_url, (err, data_url) => { this calling has callback which means that you will continue in executing the rest of the code and send a response using res.json then after qrcode finish it executes will enter the callback and send another response which is not allowed. you have multi execution for res.json you need to remove one of them and refactor your code I tried to refactor your code :

const validation = [check('name', 'Name is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })]

const toDataURL = (otpauth_url) => new Promise((resolve, reject) => {
  qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
    if(err)reject(err)
    resolve(data_url)
      res.status(200).json({
      img: data_url,
      token,
    })
  })
});
const signJwt = (payload)=>new Promise((resolve,reject)=>{
 return  jwt.sign(
      payload,
      config.get('jwtSecret'),
      { expiresIn: 3600 },
      (err, token) => {
        if (err) reject(err)
        resolve(token)
      }
    )
})
const postRequest = async (req, res) => {
  const errors = validationResult(req)
  if (!errors.isEmpty()) {
    return res.status(400).json({ errors: errors.array() })
  }
  const { name, email, password, type_of_user, question1, answer1, question2, answer2, } = req.body

  try {
    let user = await User.findOne({ email })
    user = new User({
      name,
      email,
      avatar,
      password,
      type_of_user,
      question1,
      answer1,
      question2,
      answer2,
    })
    const salt = await bcrypt.genSalt(10) // await
    user.password = await bcrypt.hash(password, salt) // await
    await user.save()
    // MFAOptions & secret will generate a secret
    const MFAOptions = {
      issuer: 'xyz', user: req.body.email, length: 64,
    }
    const secret = speakEasy.generateSecret(MFAOptions)

    const token = jwt.sign(
      {
        name: user.name,
        email: user.email,
        twofactor: false,
      },
      config.get('jwtSecret'), { expiresIn: '1h', })

    // update the user that is just created:
    await user.update({ email: req.body.email },
      { $set: { twoFASecret: secret.base32 }, }).exec()
      const  data_url= await toDataURL(secret.otpauth_url)
      if(data_url)    return   res.status(200).json({
        img: data_url,
        token,
      })
    const payload = {
      user: {
        id: user.id,
      },
    }
  const  token= await signJwt(payload)
  return res.json({token})

  } catch (err) {
    console.error(err.message)
  return  res.status(500).send('Server error')
  }
}
router.post('/', validation, postRequest)


module.exports = router