2

On my server I made accounts require email verification and to send the verification email:

Accounts.config({sendVerificationEmail: true, forbidClientAccountCreation: false});

I read somewhere online that the verification link will redirect the user to the home page of the web app once clicked.

On that home page I try to catch the first time it gets confirmed as I would like to add some entries to the MONGO DB the FIRST TIME the email gets verified and the user gets authenticated.

So I try to get this confirmation on the client side by doing this:

Template.home.created = function(){
      if (Accounts._verifyEmailToken) {
        Accounts.verifyEmail(Accounts._verifyEmailToken, function(err) {
          if (err != null) {
            if (err.message = 'Verify email link expired [403]') {
              console.log('Sorry this verification link has expired.')
            }
          } else {
            console.log('Thank you! Your email address has been confirmed.')

          }
        });
      }
    }

Unfortunately I NEVER got console.log('Thank you! Your email address has been confirmed.') to log in the console..... I always get console.log('Sorry this verification link has expired.') even after the first time I click on it.

What am I missing here???

How do I get a function to be called the first time the email gets verified???

Thank you.

preston
  • 3,721
  • 6
  • 46
  • 78
  • you can try to use `Accounts.onLogin` callback to listen when the user log in, And check if the email is verified and have a function there. –  Sep 23 '15 at 06:56
  • But that callback would be called every time the user logs in. I would like to add the mongodb entries just once the first it authenticates or log in, but absolutely only once..... – preston Sep 23 '15 at 07:00

4 Answers4

1

Your error message verification is wrong: you are doing an assignment instead of a conditional check.

if (err.message = 'Verify email link expired [403]') // WRONG!
if (err.message == 'Verify email link expired [403]') // this is a condition

I suggest you output the contents of err.message to move forward, because it may not be related to link expiration at all!

Template.home.created = function(){
  if (Accounts._verifyEmailToken) {
    Accounts.verifyEmail(Accounts._verifyEmailToken, function(err) {
      if (err != null) {
        console.log(err.message);
      } else {
        console.log('Thank you! Your email address has been confirmed.')
      }
    });
  }
}
SylvainB
  • 4,765
  • 2
  • 26
  • 39
  • Hi, I am now using ur code above but I'm still not getting console.log('Thank you! Your email address has been confirmed.') to log. This means err is still not null even though I have only clicked the verification link the first time. Any ideas what's going on? – preston Sep 23 '15 at 11:02
  • Well what do you get from the `console.log(err.message)` then? – SylvainB Sep 23 '15 at 11:07
  • Hi, I'm getting a actual error message : " Error: Verify email link expired [403] "...that is even though I just clicked the link the first time....there might be a race here....and it gets verified first before getting to that callback...but i don't know how to fix it.... – preston Sep 23 '15 at 11:14
1

Ok....after playing around with the options.....I found this to work. The only downside is that a warning shows up in the console. This is the warning:

Accounts.onEmailVerificationLink was called more than once. Only one callback added will be executed.

I believe this is because I am using accounts-ui package.....and perhaps accounts-ui uses Accounts.onEmailVerificationLink as well and now we are overriding it.....

This is the solution:

Accounts.config({sendVerificationEmail: true, forbidClientAccountCreation: false});

    if(Meteor.isClient){

      Accounts.onEmailVerificationLink(function(token, done){
        console.log('inside onEmailVerificationLink');
        console.log('token');
        console.log(token);

        Accounts.verifyEmail(token, function(err) {
          if (err != null) {
            console.log(err.message);
          } else {
            console.log('Thank you! Your email address has been confirmed.')
          }
        });

      });
    }


    if(Meteor.isServer){
      Accounts.onCreateUser(function(options, user){

         console.log('inside onCreateUser');
         return user;
      });

    }
preston
  • 3,721
  • 6
  • 46
  • 78
  • 1
    this seems wrong to me, because you're verifying email on the client. I haven't ued the email verification but as you point yourself, the account-ui package uses this method (it is defining an option for it!) so I beleive it does all this for you (i.e. the package stores it's own info under the users.service key and has a field for email, verified. I am pretty sure if you activate the option and do nothing else, you will have that value updated by the package instead of verifying it yourself. – MrE Sep 24 '15 at 15:42
  • Hi, Yes the package does the verification for you to, but it doesn't provide a 'hook' that one can use to call a custom function. This is why I needed to do it this way so I can put my custom code after console.log('Thank you! Your email address has been confirmed.') which acts like a 'hook' for me.. – preston Sep 24 '15 at 15:50
1

Two potential solutions:

Solution #1

Use monkey patching to intercept the call to the callback passed to verifyEmail() so that you can do what you want in addition to calling the original callback. Something like this (untested):

Accounts.verifyEmail = _.wrap(Accounts.verifyEmail, function (origVerifyEmail, token, callback) {
  return origVerifyEmail.call(Accounts, token, _.wrap(callback, function (origCallback, err) {
    try {
      if (! err) {
        // Add entries to the DB here
      }
    } finally {
      return origCallback.apply(null, _.rest(arguments));
    }
  }));
});

Note that if you use the above approach, you will presumably still need the server to ensure that the user's email address is in fact verified (i.e. the user's emails array contains an object with verified: true) before actually adding stuff to the DB. For that reason, you might prefer...

Solution #2

On the server, watch the Meteor.users collection for changes to the verification status of email addresses. Something like this (untested):

Meteor.users.find().observe({
  changed: function (oldUser, newUser) {
    if (! _.findWhere(oldUser.emails, { verified: true }) &&
      _.findWhere(newUser.emails, { verified: true })) {
      // Add entries to the DB here
    }
  }
});
Michael Cole
  • 15,473
  • 7
  • 79
  • 96
Dean Brettle
  • 737
  • 4
  • 10
0

If an email is already verified, verifyEmail function will return the Error: Verify email link expired [403] error.

Make sure that you are only sending the verification token to an unverified email address, if you call the resetPassword function the email will automatically be verified.

It could be that you tested the verification token once on an account and it verified the email and then when you tried again it gave you the link expired error.

A good way to test if the email is verified is to add the following snippet of code to your dashboard (or wherever the page goes when {{#if currentUser}} condition is true):

<p>
    {{#if currentUser.emails.[0].verified}}
        <p>Email is verified</p>
    {{else}}
        <p>Email is not verified</p>
    {{/if}}
</p>

Hope this helps!

Barry Michael Doyle
  • 9,333
  • 30
  • 83
  • 143