问题
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