I need a basic email verification after a user signs up.
my pseudo code for this is something like
1.user signs up
2.his data is stored in database.
3.a token is generated using crypto.
4.token is then send to email id provided.
5.user clicks the link a account is verified.
meanwhile a separate sequelize schema is created that stores the email id and the token.
now my problem is how to implement this in my project
passport.use('local-signup', new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
var generateHash = function (password) {
return bCrypt.hashSync(password, bCrypt.genSaltSync(8), null);
};
User.findOne({
where: {
email: email.toLowerCase()
}
}).then(function (user) {
if (user) {
return done(null, false, {
message: 'That email is already taken'
});
}
else {
var userPassword = generateHash(password);
var data =
{
email: email.toLowerCase(),
password: userPassword,
firstname: req.body.firstname,
lastname: req.body.lastname,
mobileno: req.body.mobileno,
//verified_email: false,
//verified_mob: false
};
User.create(data).then(function (newUser, created) {
if (!newUser) {
return done(null, false);
}
if (newUser) {
return done(null, newUser);
}
});
}
});
}
));
i am new to nodejs but with all my understanding i guess things need to be impemented in
if (newUser) {
return done(null, newUser);
}
any guidance is appreciated.
my user schema..
module.exports = function (sequelize, Sequelize) {
var User = sequelize.define('user', {
id: {
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstname: {
type: Sequelize.STRING,
notEmpty: true
},
lastname: {
type: Sequelize.STRING,
notEmpty: true
},
//username: { type: Sequelize.STRING },
//about: { type: Sequelize.TEXT },
mobileno: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING,
validate: { isEmail: true }
},
password: {
type: Sequelize.STRING,
allowNull: false
},
last_login: {
type: Sequelize.DATE
},
verified_email: {
type: Sequelize.BOOLEAN,
defaultValue: false,
},
verified_mob: {
type: Sequelize.BOOLEAN,
defaultValue: false,
}
//status: { type: Sequelize.ENUM('active', 'inactive'), defaultValue: 'active' }
});
return User;
}
At any circumstances do not share your token with any of your users. User ID's can easily be extracted by tokens and this can be harmful.
Instead, try this approach;
I assume your user model is something like this at your database
{
name: String,
auth: {
password: String //This will be bcrypted.
email: {
address: String,
verified: String || Boolean,
}
}
}
As you can see, verified field holds a String field or Boolean field.
At the moment you create user, (model.create({...}) sequence) preset the value of verified to sha256 of current time (you can use sha256(moment().format())) and save user.
At mail, send user a link like, yoursite.com/verify?code=[code] and then,
Create a route for user/verify?code=[code] in controller. Get user get the user holds code in verified field and change it to 'true'
You are done.
Related
I have some weird bug when trying to mock my database.
First, here is my User model (with default timestamp because I didnt add anything to options):
const { Sequelize, Model, DataTypes } = require("sequelize");
const sequelize = require("../db");
const User = sequelize.define("user", {
id: {
type: DataTypes.INTEGER, // or maybe Type STRING?
autoIncrement: true,
primaryKey: true,
allowNull: false,
},
first_name: {
type: DataTypes.STRING,
},
last_name: {
type: DataTypes.STRING,
},
email: {
type: DataTypes.STRING,
unique: true,
allowNull: false
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
number_of_answers: {
type: DataTypes.INTEGER,
defaultValue: 0,
}
});
module.exports = User;
I already have one user that I had inserted by Signup endpoint:
Screenshot from psql - SELECT * FROM users;
Now, I am trying to INSERT INTO users and I made a sql file with Mockaroo. Here is example of one line, and error that I am getting when I try to run this command.
psql error on INSERT INTO command
Does anyone know what is a problem here and how can I insert user in colletion via psql.
If you want to know how my endpoint for signup works, here is my code:
// User Signup
router.post("/signup", async (req, res, next) => {
try {
// Crypting password
const hashedPw = await bcrypt.hash(req.body.password, 12);
const results = await User.create({
first_name: req.body.firstName,
last_name: req.body.lastName,
email: req.body.email,
password: hashedPw,
});
res.status(201).json({
status: "User saved to database",
data: {
results: results,
},
});
} catch (err) {
next(err);
}
});
Okay, I found out that I need to put attributes in between "".. So it would be like this below..
INSERT INTO users ("id", ...,"createdAt" ..) VALUES ("idValue", ...);
I'm trying to build a user model, but I want to make sure that username and email are unique. When I created the first user everything was ok, but when I try to create the second user with the same information, I got the some error that I can handle in when I will save, but the duplicate key wasn't there to handle it.
This is my schema file code:
const UserSchema = new Schema({
// this username with SchemaType of string
username: {
type: String,
lowercase: true,
required: [true, "username is required"],
unique: true,
trim: true,
minlength: [4, "try to user longer name"],
maxlength: [60, "your name is way too long"],
},
// virtual name
name: {
// name have two properties
// first is first and refer to first-name
// second is last and refer to last-name
first: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
},
last: {
type: String,
lowercase: true,
trim: true,
minlength: 4,
maxlength: 20
}
},
password: {
type: String,
required: [true, "password is required"]
},
email: {
type: String,
required: [true, "email is required"],
unique: true
},
active: {
type: Boolean,
default: true
},
admin: {
type: Boolean,
default: false
},
meta: {
update: {
type: Date,
default: Date.now()
},
timestamp: {
type: Date,
default: Date.now()
}
}
});
UserSchema.virtual("fullname").get(function () {
// return the concatenation of first and last
return this.name.first + " " + this.name.last;
});
// Create User Model
const User = mongoose.model("User", UserSchema);
module.exports = User;
And this is my router code where I tried to handle it:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
console.log(err);
});
// save User in model
newUser.save()
// return response with info
return response.status(201).json(user);
})
I think the explanation here is quite a simple one. You are specifying the unique attribute in your schema for multiple fields, so mongo will not allow you to create multiple entries with the same information. This is quite obvious.
Also, I noticed a bit of irregularity in your code. The save method you are calling returns a promise, which means the event loop will not block your code and the response will be returned immediately. For this, you either need to handle your response inside the then block or use async await throughout your code.
I would suggest the following changes:
router.post("/register", (request, response) => {
const user = {
username: request.body.username,
email: request.body.email,
password: request.body.password
};
if (!user.email && !user.username && !user.password) {
return response.json({
"message": "please fill the whole information"
});
}
// put user info in model
const newUser = new User({
username: user.username,
email: user.email,
password: user.password
})
newUser.validate((err) => {
if(err) {
response.status(403).json({ message: 'Your custom error message' });
}
newUser.save().then(res => {
return response.status(201).json(user);
}).catch(e => {
return response.status(500).json({ message: 'Your custom error message' });
})
});
})
well, I'm trying to create a user with Sails 1.0.0-42, the problem is that the callback lifecycle of the user model is not called. I have tried in many ways and nothing is called. Thanks in advance.
this is the code of Action2 "signup":
module.exports = {
friendlyName: 'Create User',
description: 'user create action2',
inputs: {
name: {
description: 'user create',
type: 'string',
required: true
},
email: {
description: 'user create',
type: 'string',
required: true
},
password: {
description: 'user create',
type: 'string',
required: true
},
},
exits: {
notFound: {
description: 'ERRO create user.',
responseType: 'notFound'
}
},
fn: async function (inputs, exits) {
const {name, email, password} = inputs;
var user = await User.create({name, email, password}).fetch();
if(!user) return exits.notFound();
return exits.success(user);
}
};
this is the user model code
var bcrypt = require('bcrypt');
module.export = {
attributes: {
name: {
type: 'string',
required: true,
},
email: {
type: 'string',
required: true,
},
password: {
type: 'string',
minLength: 6,
required: true,
columnName: 'hashed_password'
},
},
// Lifecycle Callbacks
beforeCreate: function (values, cb) {
// Hash password
bcrypt.hash(values.password, 10, function(err, hash) {
if(err) return cb(err);
values.password = hash;
cb();
});
}
};
the user is created, but the password is not encrypted, and in the tests the beforeCreate is not called.
Your model file declares:
module.export
But it needs to be:
module.exports
That s makes a big difference!
The reason it works at all with your current code is that, by default, the built-in sails-disk database is schemaless, so even though your User.js file wasn't exporting anything, it still lets you create records with whatever fields you want.
I am using this passport-generate-auth module, and I am trying to get my grasp around understanding this whole thing.
So, in my User model, I've got
var User = {
schema: true,
attributes: {
username: {
type: 'string',
unique: true,
required: true
},
email: {
type: 'email',
unique: true,
required: true
},
password: {
type: 'string',
required: true,
minLength: 8
},
}
};
module.exports = User;
And when I call
exports.register = function (req, res, next) {
var email = req.param('email')
, username = req.param('username')
, password = req.param('password');
User.create({
username: username
, email: email
, password: password
}, function (err, user) {
if (err) {
if (err.code === 'E_VALIDATION') {
if (err.invalidAttributes.email) {
req.flash('error', 'Error.Passport.Email.Exists');
} else {
req.flash('error', 'Error.Passport.User.Exists');
}
}
return next(err);
}
});
};
};
when providing username and email that already exist in the database, the new entry is stored in the DB, instead of giving me an error msg.
Isn't User.create() supposed to take care of checking in the schema attributes rules whether they are unique and then check the records in the DB for a record with a value that already exists?
Unfortunatly the documentation for model property setters and getters is somewhat deficient and I'm having trouble getting my little setter to work.
var bcrypt = require('bcrypt');
module.exports = function( sequelize, DataTypes )
{
var User = sequelize.define('User', {
username: { type:DataTypes.STRING, unique: true, allowNull: false },
email: { type:DataTypes.STRING, allowNull: false, unique: true },
userlevel: { type:DataTypes.INTEGER, allowNull:false, defaultValue:0 },
password: { type:DataTypes.STRING,
set: function(v) {
var pw = this;
var r;
bcrypt.genSalt(10, function(err,salt) {
bcrypt.hash(v, salt, function(err,hash) {
pw.setDataValue('password', hash);
});
});
} }
});
return User;
}
Now from what I can tell based on github issues custom setters on properties are not called on create() so calling
db.User.create( { username:'guest', email:'guest#guest', userlevel:1, password:'guest' } ).success( function(record) { console.log(record) });
results in the following insert:
Executing (default): INSERT INTO `Users` (`id`,`username`,`email`,`userlevel`,`createdAt`,`updatedAt`) VALUES (DEFAULT,'guest','guest#guest',100,'2014-02-25 01:05:17','2014-02-25 01:05:17');
so I went ahead and added the following in the success clause:
u.set('password', 'stupid');
u.save();
I can see that my setter is getting properly called and that the hash is getting set on the password property. However once the setter ends and I return back to my u.save() line the u object is back to it's previous state with no password set.
Any ideas?
You are experiencing this issue, because getters and setters are currently only support synchronous actions. Saying this, you can find a working solution here:
var User = sequelize.define('User', {
username: { type: DataTypes.STRING, allowNull: false, unique: true },
email: { type: DataTypes.STRING, allowNull: false, unique: true },
userlevel: { type: DataTypes.INTEGER, allowNull:false, defaultValue:0 },
password: {
type: Sequelize.STRING,
set: function(v) {
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync(v, salt);
this.setDataValue('password', hash);
}
}
})