3

Kind of a strange issue. I am building a NodeJS/Express api using json web tokens for email verification for users. Code for both the user registration and email confirmation routes are below. When testing these routes in Postman using the following procedure, everything goes fine (status 200, user object returns): 1. Send POST request to create user. 2. Use mongodb shell to get the token from the new user. 3. Use copy/pasted token in get request to confirmation route.

However, when actually clicking the link that gets sent by email everything updates in the DB as it's meant to, but I get a 401 error. Even copying and pasting the token from the link in the email and sending it in postman has the same result.

Routes:

app.post('/api/users', (req, res) => {
  let body = _.pick(req.body, [
    'email', 'password', 'firstName', 'lastName', 'adminRequested'
  ]);
  let user = new User(body);

  user.save().then(() => {
    return user.generateAuthToken('confirmation', '1h');
  }).then((token) => {
    let currentUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
    let confirmationUrl = currentUrl + `/confirm/${token}`;
    mailTransport.sendMail({
      from: `Break Board API <${process.env.MAIL_USER}>`,
      to: user.email,
      subject: 'Your Email Confirmation Link',
      text: `Click this link to verify your email: ${confirmationUrl}`
    });
    res.send(JSON.stringify(user, undefined, 2));
  }).catch((e) => {
    res.status(400).send(e);
  });
});

app.get('/api/users/confirm/:token', confirmToken, (req, res) => {
  let token = req.token;
  let user = req.user;
  req.user.update({
    $set: {
      emailConfirmed: true
    },
    $pull: {
      tokens: {token}
    }
  }).then(() => {
    res.send(user);
  });
});

confirmToken middleware:

let {User} = require('./../models/user');

let confirmToken = (req, res, next) => {
  let token = req.params.token;

  User.findByToken(token).then((user) => {
    if(!user) {
      return Promise.reject();
    }
    req.user = user;
    req.token = token;
    next();
  }).catch((e) => {
    res.status(401).send();
  });
};

The middle ware is the only place in the entire code that has a res.status(401), so I know that is where it's coming from, but can't understand why it only happens when using the token from the email, or all the DB operations are still succeeding if it is indeed a 401. When forcing 401 errors in Postman with modified or non-existant JWT tokens, the DB operations are never performed.

Any ideas?

TylerB
  • 31
  • 1
  • 2
  • So, through a lot of trial and error, I figured out what is happening, but still not sure how to fix it. Basically, whenever I open the email, before I click on the link, it is validating the link. So by the time I click on the link, everything has been updated and the token is removed. – TylerB Dec 09 '17 at 10:29

1 Answers1

0

Okay, after getting fed up I decided to just move on with implementation and inadvertently found the solution. When I switched to rendering the email in html instead of text, the email client no longer caused the token link to be followed.

TylerB
  • 31
  • 1
  • 2