Generate new verification token

爱⌒轻易说出口 提交于 2020-01-05 05:00:15

问题


I'm would like to know how could i create a method, or if there is a method to generate a new token only with the email. I want create a option in my site "Send new verification email", where the user only needs to put the email. Actually i'm using Mandril, so i'm using a custom way to send emails and verify users:

  function generateVerificationToken(context, user, callback) {
    const { req } = context;
    req.app.models.User.generateVerificationToken(user, (error, token) => {
      if (error) {
        return callback(error, null);
      }
      callback(null, token);
    });
  }

  User.afterRemote('create', (context, user, next) => {
    generateVerificationToken(context, user, (error, token) => {
      if (error) {
        return next(error);
      }
      user.verificationToken = token;
      user.save((error) => {
        if (error) {
          return next(error);
        }
        loopback.Email.send({
          to: user.email,
          template: {
            name: 'signup-confirm',
          },
          global_merge_vars: [{
              name: 'href',
              content:`http://localhost:3000/api/accounts/confirm?uid=${user.id}&token=${token}&redirect=http://localhost:4200/login/token-verification&verification=1`
          }]
        }, (error) => {
          next(error);
        });
      });
    });
  });

Thanks in advance!


回答1:


(Note: This question is a bit tricky because it involves several modifications that, although not that hard, might require some refactoring of your code. Also, see the warning note at the end please.)

1. Override the User model

(Note: Although you could do this in another model, I found it better to do it inside User for consistency's sake, even though there's a bit more to do.)

To override the User model, you can do two things. Some people like to add a new user model (in lowercase) and do the overriding there, but I personally prefer to use Spencer Mefford's more elegant way of doing it.

You should check the whole gist because there's a lot more going on, but to summarize a bit, you need to create a new boot script, ideally with a name starting with "0" (boot scripts are executed in alphabetical order and thus you need to have the model ready before the rest of the stuff), for example

server/boot/0-user-model-override.js

Then you add the necessary boilerplate:

module.exports = function (app) {      
    var User        = app.models.User;
    var Email       = app.models.Email;
    var Role        = app.models.Role;
    var RoleMapping = app.models.RoleMapping;
    var ACL         = app.models.ACL;

    /*
    * If this is an initial setup, create the ACL entry,
    * otherwise just configure the relationships 
    * (this needs to be done every time the server is started)
    */

    if(process.env.INIT_SETUP == "true"){
        ACL.create({
            model: 'User',
            property: 'customEndpoint1',
            accessType: 'EXECUTE',
            principalType: 'ROLE',
            principalId: '$everyone',
            permission: 'ALLOW'
          }, function (err, acl) { // Create the acl
            if (err) console.error(err);
        });
    }

    RoleMapping.belongsTo(User);
    RoleMapping.belongsTo(Role);
    User.hasMany(Role, {through: RoleMapping, foreignKey: 'principalId'});
    User.hasMany(RoleMapping, {foreignKey: 'principalId'});
    Role.hasMany(User, {through: RoleMapping, foreignKey: 'roleId'});

    // Add your custom endpoints
    User.customEndpoint1 = function(param1, cb) {...};
    User.remoteMethod('customEndpoint1',...){...};
};

The boilerplate is basically there because we need to manually add an ACL entry that sets the permissions to allow anyone to request a new verification email. If this is not done, by default the User model denies access by non-authenticated users.

Also, note that we only create the ACL entry in the DB when we are doing the initial setup, i.e. we do it only once (unless you setup a new DB).

However we need to configure the relationships between the User, Role and RoleMapping every time we start the server, otherwise you will get access errors for valid users and other strange behaviors.

(Note: Although this is quite a bit of work, I think being able to somewhat easily add new functionality to the User model will allow you to keep user management where it belongs.)

After this setup you can now do e.g.

POST https://myserver/api/Users/newVerificationEmail

2. Create an endpoint to request the new link

To add an endpoint you do as you would with any other model:

User.newVerificationEmail = function(email, cb) {
  console.log("A new verification email was requested for: " + email);   
  cb(null);
};

User.remoteMethod(
  'newVerificationEmail',
  {
    accepts: [
      {arg: 'email', type: 'string'}
    ],
    http: {
      verb: 'post'
    }
  }
);

3. Call the verify method and send the email

To send the verification email you have a few options. You can either:

  • Re-use the User.verify() method (located in the user.js model file) and the default SMTP emailer
  • Re-use the User.verify() method but with your own Emailer (to send via API for example)
  • Do everything by hand, i.e. generate the token yourself, saving it to the User collection and then sending the email, which is basically what User.verify() does. However this requires you to also write the confirmation logic which is yet more work.

User.verify() with default emailer

To re-use the verify method you need to generate the verify link (except the token part, which will be added by the method itself), configure the options, and call the method.

User.newVerificationEmail = function(email, cb) {
  console.log("A new verification email was requested");
  var userModel = User.constructor;

  // Note: To get user.id you need to query the DB 
  // for the User instance with the requested email

  var verifyLink = 'https://' +
                      hostAddress + 
                      ':' + 
                      portNumber +
                      restApiRoot + 
                      '/Users/confirm' +
                      '?uid=' +
                      user.id + 
                      '&redirect=https://' + hostAddress + '/verified?user_id='+user.id;

  var options = {
      type: 'email',
      mailer: Email,
      to: user.email,
      from: 'sender@example.com',
      subject: 'My Email Subject',
      template: path.resolve(__dirname, '../views/verify.ejs'),
      user: user,
      verifyHref: verifyLink,
      host: myEmailHost,
      port: myEmailPort
  };

  user.verify(options, function(err, response) {

    if (err) {
      console.log(err);
    }
    console.log("Account verification email sent to " + options.to);
    cb(null);
  });

};

Create the email verification template

The email that will be sent is the one specified in options.template, i.e. server/views/verify.ejs

This file should contain the verification link we generated again. You can add whatever HTML you want, just be sure to add the verifyHref variable:

Please click <a href="<%= verifyHref %>">this link</a> to verify your email

After these changes are done, this should send an email whenever you do a POST request to api/Users/newVerificationLink

User.verify() with custom emailer

I haven't yet finished implementing this solution, but it basically involves creating your own Email connector to use your provider's API (e.g. Mandrill, Mailgun, etc) and passing this model in the options.mailer field.


Warning: I haven't tested this code and there are several variable values that you need to specify yourself (e.g. hostAddress, portNumber, restApiRoot, etc). The code in this answer has been extracted from several pieces of a project I'm working on and although it's almost complete, you need to verify that there are no missing callbacks and other typos and compiler errors as well as provide the code to search the User object corresponding to the provided email (which is very easy to do).



来源:https://stackoverflow.com/questions/40441368/generate-new-verification-token

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!