Mongoose not saving correct schema - javascript

I have small project to introduce myself to some front-end technologies. I am using Node, Express, Pug, and MongoDB.
I define the user schema in my user.js file:
var userSchema = mongoose.Schema({
username : String,
password : String,
jobs : [{ type: mongoose.Schema.Types.Mixed }]
});
Then, in my passport.js file I start the sign up process.
User.findOne({ 'username' : username }, function(err, user) {
// if there are any errors, return the error
if (err) {
console.log(err);
return done(err);
}
// check to see if theres already a user with that email
if (user) {
console.log('user exists');
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
} else {
console.log('creating new user...');
// if there is no user with that email
// create the user
var newUser = new User();
newUser.username = username;
newUser.password = newUser.generateHash(password);
newUser.jobs = [{ website: 'google.com' }];
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
}
});
The post successfully saves the new user as:
{
"_id": {
"$oid": "5967d2acc64d953330a3ac32"
},
"__v": 0
}
My goal is to have an array in the database where website links can be pushed into the array for that user.
Thanks for any assistance.

Set the jobs field type as array of Mixed:
var userSchema = mongoose.Schema({
local: { username : String, password : String },
jobs: [ { type: mongoose.Schema.Types.Mixed } ]
});
Then create the user passing all parameters to the constructor:
var newUser = new User({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
});
// save the user
newUser.save(function(err) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
});
You could also create the user without instantiating it first:
// save the user
User.create({
local: {
username: username,
password: User.generateHash(password),
},
jobs: [{ website: 'google.com' }]
}, function(err, newUser) {
if (err) {
console.log(err);
throw err;
}
console.log('user saved: ', newUser);
return done(null, newUser);
})
For both these methods you will probably need to make the generateHash a static method.

Related

Delete specific object in MongoDB collectionn

In MongoDB I have a User collection, where each user has a games object Array with many objects inside. I need to delete a specific game of a certain user (in this case lOrrlB).
JS
I make a Delete request where gameViewing = lOrrlB
$.ajax({
type: 'DELETE',
url: '/games/' + gameViewing,
data: {
toDelete: gameViewing
},
dataType: "json"
})
App.JS
I search for the user and try to deletOne the corresponding game, but in Node I get "Cannot read property 'gameToDelete' of undefined". Also I'm not convinced about the "key" parameter.
app.route('/games/:gameId')
.delete(function(req, res){
let gameToDelete = req.body.toDelete;
User.findById(req.user.id, function(err, foundUser) {
if (err) {
console.log(err)
} else {
if (foundUser) {
User.games[gameToDelete].deleteOne(
{key: req.body.toDelete},
function(err) {
if(!err){
console.log("Deleted!");
} else {
console.log("Error!");
}
}
)
}
}
})
});
Model
const userSchema = new mongoose.Schema({
googleId: String,
profileImage: String,
myCollection: {
type: Object,
default: Object
},
games: {
type: Object,
default: Object
}
});
userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);
const User = new mongoose.model("User", userSchema);
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/12345",
userProfileUrl: "https://www.googleapis.com/oauth2/v3/userinfo",
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({
googleId: profile.id,
username: profile.displayName,
profileImage: profile.photos[0].value,
myCollection: catalogDb,
games: {}
},
function(err, user) {
return cb(err, user);
});
}
));
Look like $unset is what you need:
User.findByIdAndUpdate(req.user.id, {
$unset: {
[`games.${gameToDelete}`]: ""
}
}, ...)

Check if user already exists in mongodb and passport

I'm new to passport and I'm trying to create a "register" page. This actually works fine, and the log-in form as well. However, I want to check if the username entered already exists, and if it does, throw an error to the user. Here is my code so far:
expressApp.post("/register", function(request, response){
User.findOne({username: request.body.username}, function(err, user) {
if (err) {
return err;
}
if (user) {
}
else {
User.register(new User({
username: request.body.username,
type: "Student"}),
request.body.password, function(err){
if(err){
console.log(err);
}
passport.authenticate("local")(request, response, function(){
response.redirect("/");
});
});
}
})
});
However, If someone chooses a username that already exists, then i want to be able to tell them that there is an error.
It should look something like this.
expressApp.post("/register", function(request, response) {
User.findOne({
username: request.body.username
}, function(err, user) {
if (err) {
return err
} else if (user) {
//user.message = "User already exists!!"
response.statusCode = 409
return response.send({"message": "User already exists!!")
} else {
User.register(new User({
username: request.body.username,
type: "Student"
}),
request.body.password,
function(err) {
if (err) {
console.log(err);
}
passport.authenticate("local")(request, response, function() {
response.redirect("/");
});
});
}
});
});

Mongoose gives no response when updating object in Mongo

I have a simple ExpressJS/Node backend that contains a MongoDB database for which I use mongoose to interact. I can add objects to the db based on the UserSchema:
const userSchema = mongoose.Schema({
email : {
type: String,
required: true,
trim: true,
unique: 1
},
password : {
type: String,
required: true,
minlength: 5
},
name : {
type: String,
required: true,
maxlength: 30
},
lastname : {
type: String,
required: true,
maxlength: 30
},
cart : {
type : Array,
default: []
},
history : {
type: Array,
default: []
},
role : {
type: Number,
default : 0
},
token : {
type: String
}
});
From the express Server, I can register and add a new user to the DB and I know this works
Server.js
//========================================
// Register User
//========================================
app.post('/api/users/register', (req, res) => {
//create new User
const user = new User(req.body);
//save user
user.save((err, doc) => {
if(err)
return res.json({success: false, err});
res.status(200).json({
success : true,
userdata: doc
});
});
})
In User.js
//========================================
// SAVE in DB
//========================================
const User = mongoose.model('User', userSchema);
Now when I want to login, operation where I need to check the email and password match I encounter a problem when everything is fine and I want to add the JWT to the object all is good until it gets to the save method, there nothing happens and it doesn't respond anymore. It's like it goes in an infinite loop. I get error when something is wrong, but on the positive case, it disappears and sends no response, to either mongo, node, debug anything.
Server.js
app.post('/api/users/login', (req, res) => {
//find the email for the user
User.findOne({'email' : req.body.email} , (err, user) =>{
if(!user)
return res.json({loginSuccess : false, message : 'Authentication failed, email not found'});
//check the password
user.comparePassword(req.body.password, (error, isMatch) => {
if(!isMatch)
return res.json({loginSuccess : false, message : 'Wrong password'});
//generate token
user.generateToken((err, user) => {
if(err)
return res.status(400).send(err);
//store token as a cookie
res.cookie('w_auth', user.token).status(200).json({
loginSuccess : true
})
})
})
})
})
User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const SALT_I = 10;
require('dotenv').config();
//========================================
// User Login
//========================================
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(error, isMatch){
if(error)
return cb(error);
cb(null, isMatch);
})
}
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(),process.env.SECRET)
user.token = token;
user.markModified('anything');
user.save(function(err,user){
if(err) return cb(err);
cb(null,user);
})
}
I get no more feedback in node console, debug, Mongo or even Postmen(I can wait here for minutes ) after user.save(...). I know it gets the good user and everything but I don't really know where to get from here. Also in Mongo I see no field for the token, I initially add an object with no token, can this affect everything? Is there another procedure to update an existing object in the collection?
In case GitHub is needed to see the code: Link
Indeed it's really strange, couldn't really debug what's wrong with this 'save' method. As a workaround, however, this one seems to work fine:
userSchema.methods.generateToken = function (cb) {
var user = this;
var token = jwt.sign(user._id.toHexString(), "mystupidsecret");
console.log("in generateToken");
console.log(user);
user.token = token;
console.log(user.token);
var email = user.email;
//save token
User.updateOne({ _id: user._id }, { $set: { token: token } }, function(err, user){
if(err) {
console.log(err);
return cb(err);
}
cb(null, user);
// this one is for debug only!
User.findOne({'email' : email} , (err, user) =>{
console.log("After update: ", user)
});
});
console.log('done');
}
It yields the following:
After update: { cart: [],
history: [],
role: 0,
_id: 5f3e48f09c7edc3f1c24a860,
email: 'abc233#wp.pl',
password:
'$2b$10$iDeeehLOzbQi3dawqW8Lg.HPOvcRBDIS/YD9D1EmqBOH9Be31WpX2',
name: 'ABCDEFGH',
lastname: 'Doeasdasdas',
__v: 0,
token:
'eyJhbGciOiJIUzI1NiJ9.NWYzZTQ4ZjA5YzdlZGMzZjFjMjRhODYw.aH9tCMbIK9t3CReiQg3Azln9Ca8xS7W0xL3qCMOKniY' }

Expected "payload" to be a plain object : MEAN

This is my code from routes file(users.js)
User.findOne({linkedin_id: req.body.linkedin_id}, function(err, linkedinUser) {
if(err) {
console.log('err in finding linkedin user '+err);
}
// if user exits
else if(linkedinUser) {
console.log('user exist');
const token = jwt.sign(linkedinUser, config.secret, {expiresIn: 604800});
res.json({success: true, token: 'JWT '+token, user: {
id: linkedinUser._id,
linkedin_id: linkedinUser.linkedin_id,
name: linkedinUser.name,
username: linkedinUser.username,
email: linkedinUser.email,
lkprofilePic: linkedinUser.profilePic
}, msg: 'user exits'
});
}
// if user doesn't exist
else {
User.create({
linkedin_id: req.body.linkedin_id,
name: req.body.name,
username: req.body.username,
email: req.body.email,
lkprofilePic: req.body.lkprofilePic
}, function(err, result) {
if(err) {
res.json({success: false, msg: 'failed to add'})
console.log('error in adding the data '+err);
}
else if(result) {
const token = jwt.sign(linkedinUser,config.secret,{ expiresIn: 604800 });
res.json({success: true, token: 'JWT '+token, user: {
id: result._id,
linkedin_id: result.linkedin_id,
name: result.name,
username: result.username,
email: result.email,
lkprofilePic: result.profilePic
}, msg: 'User added ' });
}
});
}
});
This from the config -> secret
module.exports = {
secret: 'bigfish'
}
This is the error I'm getting in the nodejs console
Receiving linkedin data
D:\product\project-1\node_modules\mongodb\lib\utils.js:132
throw err;
^
Error: Expected "payload" to be a plain object.
at validate (D:\product\project-1\node_modules\jsonwebtoken\sign.js:34:11)
at validatePayload (D:\product\project-1\node_modules\jsonwebtoken\sign.js:56:10)
at Object.module.exports [as sign] (D:\product\project-1\node_modules\jsonwebtoken\sign.js:108:7)
at D:\product\project-1\routes\users.js:415:29
at Function. (D:\product\project-1\node_modules\mongoose\lib\model.js:4177:16)
at parallel (D:\product\project-1\node_modules\mongoose\lib\model.js:2230:12)
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\parallel.js:35:9
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\once.js:12:16
at iteratorCallback (D:\product\project-1\node_modules\mongoose\node_modules\async\eachOf.js:52:13)
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\onlyOnce.js:12:16
at D:\product\project-1\node_modules\mongoose\node_modules\async\internal\parallel.js:32:13
at apply (D:\product\project-1\node_modules\lodash_apply.js:15:25)
at D:\product\project-1\node_modules\lodash_overRest.js:32:12
at callbackWrapper (D:\product\project-1\node_modules\mongoose\lib\model.js:2199:11)
at D:\product\project-1\node_modules\mongoose\lib\model.js:4177:16
at model.$__save.error (D:\product\project-1\node_modules\mongoose\lib\model.js:359:7)
But the data is getting saved in the database & doesn't return the
res.json({success: true, token: 'JWT '+token, user: {
id: result._id,
linkedin_id: result.linkedin_id,
name: result.name,
username: result.username,
email: result.email,
lkprofilePic: result.profilePic
}, msg: 'User added ' });
The issue is with the way you signed your token
The user you are using is a returned user from mongoose so you will need to use YOUR_USER.toJSON. if the user is not coming from mongoose use JSON.stringify(YOUR_USER) instead
change your code to either
const token = jwt.sign({linkedinUser}, config.secret, {expiresIn: 604800});
//if you want to set expiration on the token
OR
const token = jwt.sign(linkedinUser.toJSON(), config.secret);
//if you just want to sign the token without setting the expiration
const token=jsonwebtoken.sign(user.toJSON(),config.secret,{expiresIn:30});
add .toJSON() with your object then it will be ok

How can I reset or empty req.user in a node passport app?

I'm trying to authenticate a user when logging in to website. Upon login, the username and password get sent to a post route from an ajax request on the client side. The problem is that there is an old userID attached to req.user._id that I cannot remove. This old userID continues to authenticate any user I try.
This is my passport settings on the server:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var strategy = new BasicStrategy(function(username, password, callback) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
callback(err);
return;
}
if (!user) {
return callback(null, false, {
message: 'Incorrect username.'
});
}
user.validatePassword(password, function(err, isValid) {
if (err) {
return callback(err);
}
if (!isValid) {
return callback(null, false, {
message: 'Incorrect password.'
});
}
return callback(null, user);
});
});
});
passport.use(strategy);
app.use(passport.initialize());
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 route:
app.post('/authenticate', passport.authenticate('basic', { session: false }), function(req, res) {
console.log("req.user:", req.user);
var id = req.user._id;
res.json({
message: 'You have been authenticated',
userID: id
});
});
On the client side, I am sending a POST request like this:
function getLogin(loginObject) {
$.ajax({
type: "POST",
url: "/authenticate",
dataType : "json",
data: { username: loginObject.username, password: loginObject.password }
})
.done(function(result) {
console.log("Authenticated");
loginUserID = result.userID;
})
.fail(function(jqXHR, error, errorThrown) {
};
This is a screenshot of my terminal showing the userID that won't delete:
req.user: { _id: 588a834c08c038342e420568,
username: 'mm',
password: '$2a$10$Khp1wUkHvLLn6ZVBNlLqM.Mtio0ZZ4dznGPQs0ECqf.snhUl44OxK',
__v: 0 }
From my understanding, the req.user._id is created as soon as the username and password get's authenticated. So I cannot understand what is holding on to this userID.
Your help is appreciated.

Categories

Resources