NodeJS + Mongoose , 'unique' field attribute not working? - node.js

I am making sure that my db doesnt have duplicate usernames or email addresses for my users using the unique: true attribute, but I am able to create users with duplicate usernames...so something isn't working. I have tried dropping the database, this did not solve the issue. Also in Model.on('index'), sometimes the error is not outputted to the console, sometimes it is. Strange.
var userSchema = mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, required: true, select: false},
emailAddress: {type: String, unique: true, required: true},
emailVerified: {type: Boolean, default: false},
emailVerificationCode: {type: Number},
friends: []
});
userSchema.pre('save', function(next) {
var user = this;
if (!user.isModified('password')) return next();
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
var User = mongoose.model('User', userSchema);
User.on('index', function(err) {
console.log(err);
});
module.exports = User;
And my node router for adding users to the db...
router.post('/register', function(req, res, next) {
var newUser = new User({ username: req.body.username, password: req.body.password, emailAddress: req.body.email });
newUser.save(function(err, newUser) {
if (err) return next(err);
newUser.sendVerificationEmail(function(err, isSent) {
if (err) return next(err);
res.json({ success: true });
});
});
});

Related

Serializing and Deserializing other than user model

I have two schemas one for farmers and other for users but when serializing it is not working..
when in the registered all of the data goes to the Users model
const farmerSchema = new mongoose.Schema({
username: String,
password: String
});
farmerSchema.plugin(passportLocalMongoose);
const Farmer = mongoose.model("Farmer", farmerSchema);
passport.use(Farmer.createStrategy());
passport.serializeUser(Farmer.serializeUser());
passport.deserializeUser(Farmer.deserializeUser());
const userSchema = new mongoose.Schema({
username: String,
password: String,
pattam: [{
name: {
type: String,
unique: true
},
latitude: String,
longitude: String,
pattamdetails: String,
pattamamount: String,
pattamimg: {
data: Buffer,
contenttype: String
}
}]
});
userSchema.plugin(passportLocalMongoose);
const User = mongoose.model("User", userSchema);
passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
//in the register route
app.post("/register", function(req,res){
const farmerOrNot = req.body.radio;
if(farmerOrNot === "yes"){
Farmer.register({username: req.body.username},
req.body.password, function(err,farmer){
if(err){
console.log(err);
res.redirect("/register");
}else{
passport.authenticate("local")(req, res, function(){
res.redirect("/farmersPage");
});
}
});
}
else{
User.register({username: req.body.username},
req.body.password, function(err,user){
if(err){
console.log(err);
res.redirect("/register");
}else{
passport.authenticate("local")(req, res, function(){
res.redirect("/usersPage");
});
}
});
}
});
I want there to be different collections and for each of them to serialize and deserialize ..
Im new to development so don't know if there is any wrong

How to change collection name in mongoose.model?

Am new in Nodejs, Am trying to create user login system with mongoDB and passportjs. Here i want to change collection name.
i refer this video here
This is my code:
Database connection js
config/database.js
module.exports = {
database : 'mongodb://localhost:27017/my_first_project',
secret : 'Mysecret'
}
models/admin.js
const mongoose = require('mongoose');
let AdminSchema = mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
username:{
type: String,
required: true
},
password:{
type: String,
required: true
},
created_on:{
type: Date,
default: Date.now
},
status:{
type: Number,
required: true,
validate : {
validator : Number.isInteger,
message : '{VALUE} is not an integer value'
}
}
});
const Admin = module.exports = mongoose.model('User', AdminSchema);
config/passport.js
const LocalStorage = require('passport-local').Strategy;
const User = require('../models/admin');
const config = require('../config/database');
const bcrypt = require('bcryptjs');
module.exports = function(passport){
//Local Strategy
passport.use(new LocalStorage(function(username, password, done){
//Check username
let query = {username:username};
console.log(query);
User.findOne(query, function(err, user){
console.log(user);
if(err) throw err;
if(!user)
{
return done(null, false, {message: 'No user found'});
}
//check password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Wrong password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
This is my database structure
When i user correct username and password for login passportjs shows null.
Need to change collection name as admin.
I don't know how to ask this question. If am asking wrong sorry for all.
You can use this structure.
API structure of mongoose.model is this:
Mongoose#model(name, [schema], [collection], [skipInit])
which means
mongoose.model('User', AdminSchema, 'admin');
//Here 3rd argument 'admin': as collection name
You can do with this too
let AdminSchema = mongoose.Schema({
username : String,
password : String.
.......
}, { collection: 'admin' });
See this link from the Mongoose documentation.

Authentication issue in Nodejs: ValidationError: Path `password` is required

I am getting an error ValidationError: Path password is required, when trying to register a new User. I am new to Node, programming, and authentication.
Here is the code I have, and then I will walk through what I have done to try to trouble shoot it.
User model and schema
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require("bcrypt-nodejs");
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true},
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
UserSchema.plugin(passportLocalMongoose);
//save function -- hashes and then saves the password
UserSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
//compares and checks password
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model("User", UserSchema);
Here is my app.js file:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
expressSanitizer = require("express-sanitizer"),
passport = require("passport"),
cookieParser = require("cookie-parser"),
LocalStrategy = require("passport-local"),
session = require("express-session"),
nodemailer = require("nodemailer"),
bcrypt = require("bcrypt-nodejs"),
async = require("async"),
crypto = require("crypto"),
flash = require("connect-flash"),
moment = require("moment"),
User = require("./models/user"),
// seedDB = require("./seeds"),
methodOverride = require("method-override");
// APP CONFIG
mongoose.connect("mongodb://localhost/blog", {useMongoClient: true});
//PRODUCTION CONFIG - LIVE URL GOES HERE!
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressSanitizer());
app.use(methodOverride("_method"));
app.use(cookieParser('secret'));
//require moment
app.locals.moment = require('moment');
// seedDB(); //seed test data!
// PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "It's a secret to everyone!!",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
console.log(user);
User.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect password.' });
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// REQUIRE ROUTES
var commentRoutes = require("./routes/comments"),
bpostRoutes = require("./routes/bposts"),
indexRoutes = require("./routes/index");
//USE ROUTES
app.use("/", indexRoutes);
app.use("/bposts", bpostRoutes);
app.use("/bposts/:id/comments", commentRoutes);
//RUN SERVER
app.listen(process.env.PORT, process.env.IP, function(){
console.log("The Server Has Started!");
});
Here is the index.js route file for posting new Users
// handle sign up logic
router.post("/register", function(req, res) {
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
});
console.log(newUser);
// if (req.body.adminCode === 'secretcode123') {
// newUser.isAdmin = true;
// }
User.register(newUser, req.body.password, function(err, user) {
if (err) {
console.log(err);
return res.render("register", { error: err.message });
}
passport.authenticate("local")(req, res, function() {
req.flash("success", "Successfully Signed Up! Nice to meet you " + req.body.username);
res.redirect("/bposts");
});
});
});
As you can see in the index.js
I did a console.log(newUser) and it brings back the entire newUser object.
{ username: 'apple',
firstName: 'apple',
lastName: 'apple',
email: 'apple#gmail.com',
_id: 5a2eac41720dc60acffa02f4 }
Outside of this I am not sure what other testing I could do to locate whats happening behind the scenes. Any help or pointers on how to think about this kind of problem in the future so I can resolve it myself would be awesome. Thanks.
Here is the full StackTrace as well if it helps:
{ ValidationError: User validation failed: password: Path `password` is required.
at MongooseError.ValidationError.inspect (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validation.js:57:23)
at formatValue (util.js:351:36)
at inspect (util.js:185:10)
at exports.format (util.js:71:24)
at Console.log (console.js:43:37)
at /home/ubuntu/workspace/routes/index.js:35:21
at /home/ubuntu/workspace/node_modules/passport-local-mongoose/index.js:213:33
at /home/ubuntu/workspace/node_modules/mongoose/lib/model.js:4038:16
at /home/ubuntu/workspace/node_modules/mongoose/lib/services/model/applyHooks.js:175:17
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
errors:
{ password:
{ ValidatorError: Path `password` is required.
at MongooseError.ValidatorError (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validator.js:25:11)
at validate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:782:13)
at /home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:829:11
at Array.forEach (native)
at SchemaString.SchemaType.doValidate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:789:19)
at /home/ubuntu/workspace/node_modules/mongoose/lib/document.js:1528:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
message: 'Path `password` is required.',
name: 'ValidatorError',
properties: [Object],
kind: 'required',
path: 'password',
value: undefined,
reason: undefined,
'$isValidatorError': true } },
_message: 'User validation failed',
name: 'ValidationError' }
Basically you defined that you need a password in your models.
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true}, // <-- See here
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date
});
But when you are saving you aren't passing it a password:
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
/* password: req.sanitize(req.body.password) // <- Probably missing something like this.*/
});
I didn't write the following blog, but I think it might be helpful for you.
https://github.com/DDCreationStudios/Writing/blob/master/articles/AuthenticationIntro.md#user-registration
The way you did the passport authentication, it does not need to have the password required true in the user schema. Passport will automatically check the password if it's missing. If password is missing, passport will throw an error. So no need to make the password required true because passport will do the same job internally. This worked for me.
user.js
var userSchema=new mongoose.Schema({
username:{type:String,required:true},
email:{type:String,required:true},
password:String
});
server.js
app.post("/signUp",(req,res)=>{
User.register(new User({username:req.body.username,email:req.body.email}),req.body.password,(err,user)=>{
if(err){
console.log(err);
}else{
passport.authenticate("local")(req,res,()=>{
res.redirect("/");
});
}
});
});
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: String ,//remove previous line and just add String to password field.
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
You had this issue because you set the password field in your schema to
password: {type: String, required: true},
. You do not need that.
Use the info below and you will be fine.
password: {type: String}
See the working code below:
**user.js**
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new mongoose.Schema({
username: {type: String, required: true},
password: {type: String} // no need to add required!
});
**server.js**
app.post('/register', (req, res) => {
const { username, password } = req.body;
User.register(new User({ username }), password, (err,user) => {
if (err) {
return res.render('register', { message: err.message, messageType:
'error', user: req.user });
}else{
passport.authenticate('local')(req, res, () => {
res.redirect('/dashboard');
});
}
});
});

bcrypt login is not working in production while works on localhost

I used the user model like below:
the usermodel uses bcrypt authentication and is working fine over the localhost.
In the production, the registration works fine and while registering the user gets logged in. However, in the login it gives the error "Either Username or Password is wrong".
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require('bcrypt-nodejs');
var userSchema = new mongoose.Schema({
username: {type: String, required: true, unique: true},
email : {type: String, required: true, unique: true},
password : {type: String},
twitterId : String,
twitterToken: String,
profileUserName: String,
displayName : String,
coachingUser: Boolean,
coaching_name: String,
resetPasswordToken: String,
resetPasswordExpires: Date
});
userSchema.plugin(passportLocalMongoose);
userSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
console.log("in the presave method");
if (!user.isModified('password'))
{
//console.log("in the presave method");
return next();
}
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
console.log("in the presave method");
if (err) return next(err);
user.password = hash;
next();
});});});
userSchema.methods.comparePassword = function(candidatePassword, cb) {
console.log("candidatePassword: " + candidatePassword);
console.log(this.password);
var passlen = this.password;
console.log("password length: " + passlen.length);
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
console.log("isMatch: " + isMatch);
if (err) return cb(err);
cb(null, isMatch);
});
};`
module.exports = mongoose.model("User",userSchema);
in the localhost, the bcrypt login is working fine. However in the production over https://mlab.com/home and digitalocean, the login always gives Email or password not correct.
router.post('/login', function(req, res, next) {
console.log(req);
passport.authenticate('local', function(err, user, info) {
if (err) return next(err)
if (!user) {
//console.log(user);
req.flash("error", "Either Username or Password is wrong");
return res.redirect('/login');
}
req.logIn(user, function(err) {
if (err) return next(err);
{
//console.log(user);
req.flash("success","Successfully logged in as " + user.username);
res.redirect('/coaching');
}
});
})(req, res, next);
});
Kindly provide help as to why in production the particular logic not work

Mongoose - multiple parameters in findOne

Here is a route I have:
router.get('/home', function (req, res) {
User.findOne({_id: req.user._id}, function (err, user) {
if (err) return next(err);
res.render('home', {user: user});
});
});
Basically, in order for someone to view this /home page they need to be logged in, which is where User.findOne comes into play. It will search for the user in the 'user' collection; if it doesn't find the user (if the user isn't logged in), it will return an error. Otherwise, it will show them the /home page.
Now, I want to have a separate Admin section of my website where only users with admin privileges can access the page. I've tried doing something like this:
router.get('/admin', function (req, res) {
User.findOne({_id: req.user._id, admin: true}, function (err, user) {
if (err) return next(err);
res.render('admin', {user: user});
});
});
What I'm trying to get the code to do is to look for 2 parameters: whether the user is logged in, and whether or not in that user document their 'admin' is set to 'true'. Obviously the above code doesn't work, and I don't know how to get this to work better.
EDIT: my user schema:
var schema = mongoose.Schema;
var UserSchema = new schema ({
username: {type: String, unique: true},
email: {type: String, unique: true, lowercase: true, required: true},
password: {type: String, minlength: passMinLength, required: true},
admin: {type: Boolean, default: false},
profile: {
firstName: {type: String, default: ''},
lastName: {type: String, default: ''}
}
});
there is nothing wrong in the query {_id: req.user._id, admin: true} , and it should work if User.Schema contains the admin(Boolean) field also.
Besides, alternate way is to check for admin once you get the User object.
User.findOne({_id: req.user._id}, function (err, user) {
if (err) return next(err);
if(!user){
//considering admin is boolean field
if(!user.admin){
// Not Authorised to access, do something
}
else{
// User verified as Admin
res.render('admin', {user: user});
}
}
// UserId Not found, do something
});

Resources