Creating a User and authenticating using passport.js - node.js

I am trying to save a User and Register it at the same time. I am having troubles doing both simultaneously.
My current Schema:
const userSchema = new mongoose.Schema({
username: String,
password: String,
firstName: String,
lastName: String,
player: {
type: Boolean,
default: true
}
});
userSchema.plugin(passportLocalMongoose);
module.exports = (User, "userSchema");
My current form:
<form action="/player" method="POST">
<input type="text" name="user[username]" placeholder="username">
<input type="password" name ="user[password]" placeholder="password">
<input type="text" name="user[firstName]" placeholder="First Name">
<input type="text" name="user[lastName]" placeholder="Last Name">
<button>Submit</button>
</form>
I am requiring in express, passport, passport-local, passport-local-mongoose, body-parser, mongoose, and my schemas.
My app.js after that is:
mongoose.connect(database);
app.use(bodyParser.urlencoded({extended: true}));
app.use(require("express-session")({
secret: "Generic Secret Password",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serialUser());
passport.deserializeUser(User.deserializeUser());
//route
app.post("/player", function(req, res){
var newUser = new User({ username: req.body.user.email });
User.register(newUser, req.body.user.password, function(err, user){
if(err){
console.log(err);
return res.render("new");
}
passport.authenticate("local")(req, res, function(){
res.redirect("/player");
});
});
The problem I am having is that when I submit, the User saves the salt, hash, username, and sets player to "true", but it doesn't save lastName or firstName and it follows with a bad request(400). I can do a User.Create and it saves everything (minus the hash and salt) and redirects fine. I can also set the form up so that "user[username]" and "user[password]" are "username" and "password" and get it to save everything but firstName and lastName and it will redirect properly without a 400 status. I have tried changing the default inputs for "usernameField" and "passwordField", but I have had no luck. I've tried to look at various SO posts and the closest I found was a similar suggestion to change the defaults, so maybe I am doing it wrong. I've also tried to redirect to different routes as well and still no luck.
This is the first project I've worked on that isn't a code-along and I've tried to be as specific as possible on everything and I've been struggling for a solid eight-hours. Thanks for any help!

You have to specify the different parameters
var newUser = new User({ username: req.body.user.email,
firstName: req.body.user.firstName,
lastName: req.body.user.lastName});

you have to specify first name and lastname here var newUser = new User({ username: req.body.user.email, firstname: req.body.user.firstname, lastname:req.body.user.lastname});

Related

BAD REQUEST with passport.authenticate()

I'm a little new to using passportjs in authenticating users and i'm trying to register a user on a simple application i'm building and i keep getting this error "Bad Request" in the browser. The user does get created in the DB but the redirect doesn't work. Any help would be greatly appreciated. Sorry if this question has been asked before :)
I have attached some bits of my code.
User.register(new User({username : req.body.user_email}), req.body.user_password, function(err, user){
if(err) {
return res.render("register-user", { userDetails : user})
}
passport.authenticate("local")(req, res, function(){
res.redirect("/profile");
})
})
My user Schema is as follows:
const usersSchema = new mongoose.Schema({
username : String,
password : String
})
So i did a little more digging and i found the answer to my problem. It seems to be that passportjs requires the form inputs to have username and password as their name attributes before it will work.
From my observation, the user object argument was not complete, you didn't include the password path, check the fixed code below:
User.register(new User({
username : req.body.user_email,
password: req.body.user_password}),
function(err, user){
if(err) {
return res.render("register-user", { userDetails : user})
}
passport.authenticate("local")(req, res, function(){
res.redirect("/profile");
})
})
I had the same issue. The solution to this problem is making the form inputs have exactly username and password as the name attribute. The new user should also have this format
<input type="email" name="username">
<input type="password" name="password">
and in your js file
const user = new User({
username: req.body.username,
password: req.body.password
})

MongoError: E11000 duplicate key error collection: tracker-db.users index: username_1 dup key: { username: null }"

A bunch of similar questions answered before but none of them seem to fix my problem. No problem in adding the first user. However, the username doesn't display the records and gives error on adding the second user.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema(
{
username: {
type: String,
unique: true,
required: true,
minlength: 3,
},
gender: String,
age: Number
},
{
timestamps: true
}
);
const User = mongoose.model('user', userSchema);
module.exports = User;
The error that I'm getting
The record on adding first user
Simply drop the collection and make the request again . This should work just fine.
In Robo3T I had to look up my collections indexes, and manually delete the username index.
add a username to your schema, i had the same problem and i did this
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ username: profile.displayName, googleId: profile.id }, function (err, user) {
return cb(err, user);
});
i simply added a username in my findorcreate function so that it does not show username null
Deleting the collection and recreating the collection in mongodb worked for me
Try this out, I wish it will be helpful.
Creating a new Database in MongoDB server and saving the data in that new location, worked for me :-)
the problem is not in this module you presented here.
We need to see your code from your.js file assuming you are using express and body-parser to make all this work. I think and i made a little trial of your app and i can tell that your REQ POST using POSTMAN is incorrect. WHY ? Because you are sending a POST REQ to
http://localhost:3000/users/add
and you are setting PARAMS with username: randomguy
and that's why you're getting that error from MongoDB
{username: null}.
To fix this issue in POSTMAN you must leave PARAMS EMPTY (REMOVE ALL KEY/VALUE FIELDS) and instead make the POST request including KEY/VALUE in BODY Field:
username: randomGuy
In my case i'm using body-parser like this:
app.use(bodyParser.urlencoded({ extended: true }));
and in POSTMAN in BODY select the radiobutton x-www-form-urlencoded
/PARAMS is used for GET req/
POSTMAN POST REQ TO local MongoDB with BODY KEY/VALUE

Save additional field to mongodb using passport local strategy

I am new to web development.
I am following a Node, Express, Passport and MongoDB tutorial (there is a question about this specific tutorial on SO but I am unable to apply the solution to my problem.)
Passport provisions saving email and password. I want to add a displayName. I have added this attribute to my schema. Added a field for it in the signup form. Then tried to save it via the localStrategy when creating a new User. The email and password save successfully however displayName does not.
in user.js userSchema is defined as
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
displayName : String,
}
});
module.exports = mongoose.model('User', userSchema);
the sign up form is in signup.ejs
<form action="/signup" method="post">
<div class="form-group">
<label>Name</label>
<input type="text" class="form-control" name="displayName">
//I want to save this to local.displayName in userSchema
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" class="btn btn-warning btn-lg">Signup</button>
</form>
routes.js to process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/profile', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
passport.js
passport.use('local-signup', new LocalStrategy({
//by default, local strategy uses username and password, we will override
//with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function(req,displayName,email,password, done) {
//asynchronous
//User.findOne wont fire unless data is sent back
process.nextTick(function() {
//find a user whose email is the same as the forms email
//we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
//check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
//if there is no user with that email
//create the user
var newUser = new User();
//set the user's local credentials
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
// i need help with this.
newUser.local.displayName = req.body.displayName;
//save the user
newUser.save(function(err) {
if(err)
throw err;
return done(null, newUser);
});
}
});
});
}));
I want to save the value entered for displayName in the form to local.displayName at the time of sign up.
I have tried:
newUser.local.displayName = req.body.displayName; did not work
also,
var displayName = req.body.displayName; did not work
I have tried the following solutions to no avail:
Using PassportJS, how does one pass additional form fields to the local authentication strategy?
how can i store other form fields with passport-local.js
Update or add fields in passport.js local-strategy?
EDIT: console.log output of req.body.displayName and newUser.local.displayName
Ok, turns out it was working to begin with. I use Robo 3T to look inside my database and refreshing the connection was not enough to reflect the change. After reestablishing a connection with the host I was able to see the updated db.

Bad request while using passport in express

While integrate passport to my code's login form. Everything is working fine until i call passport.authenticate in the request, 400 Bad Request was returned. What i am doing wrong?
Strategy
passport.use('local.login',new LocalStrategy({
usernameField: 'Email',
passwordField: 'Password',
},function (email,done) {
Schema.Users.findOne({'Email': email},function (err,user) {
if(err) {
return done(err);
}
if(user==null) {
return done(null,false,{ message: 'Incorrect username.' })
}
if(user.Password!==Password) {
//console.log('Wrong password');
return done(null,false,{ message: 'Wrong password' })
}
return done(null,user);
})
}));
passport.serializeUser(function (user,done) {
done(null,user.id);
});
passport.deserializeUser(function (id,done) {
Schema.Users.findById(id,function (err,user) {
done(err,user);
})
});
Inside login.js
router.post('/x',passport.authenticate('local.signup',{
successRedirect: '/success',
failureFlash: '/failure'
}));
app.js
let login = require('./login.js');
app.use('/login',login);
HTML
<form action="http://localhost:8080/login/x" method="post">
<div class="row">
<div class="col s6">
<label for = "email"></label>
<input id = "Email" type="email" placeholder="Email" name="Email">
</div>
<div class="col s6">
<label for = "Password"></label>
<input id = "Password" type="Password" placeholder="Password" name="Password">
</div>
</div>
<button class="waves-effect waves-light btn" type="submit">Log In</button
</form>
Oh I see now, but I might be wrong. You have checking if password of user from db matches password sent from your front-end but you forgot to pass it inside this function, you have only an email there, so simply try to add password as an argument:
function (email, password, done) {
Schema.Users.findOne({'Email': email},function (err,user) {
if(err) {
return done(err);
}
if(user==null) {
return done(null,false,{ message: 'Incorrect username.' })
}
if(user.Password!==password) {
//console.log('Wrong password');
return done(null,false,{ message: 'Wrong password' })
}
return done(null,user);
})
Basically i didn't read the part of passport where it says by default it will accept only parameters named as username and password but i was trying to name it as Email and Password(notice the capital P)
Let's summarise it guys.
First, make sure if you're using any json parser in you express middleware. Body-parser looks depricated, so hust make sure you have this line in your code: app.use(express.json()).
Second, sometimes people get 400 from passport.authenticate because of credentials. So make sure your axios requests from frontend to backend has this {withCredentials: true} as a parameter.
And finally, make sure to use "username" and "password" spelled exactly like this in your userSchema, in your frontend input names, and in your strategy options. I used passport-local-mongoose, and looks like there's no need to configure local strategy, cause it use "username" and "password" by default.
Try any of this, 99% you'll be able to authenticate user and finally move on. Later, you'll find a way to specify "username" and "password" to be different, but for now I'm sure, your primary mission is to unstack)

req.user is undefined in Sean.js

I'm working on a project using Sean.js and recently I tried to add a middleware to secure my api routes. I followed the "article" example using the server.policy.js and when I try to use the req.user variable it says that is undefined. I started to investigate the problem and it comes that is something to do with passport.js. I have no more than 2 months working on this and I'm not very familiar with all of this.
On the user.server.config.js is where Serelize and Deserialize the sessions:
module.exports = function(app, db) {
// Serialize sessions
passport.serializeUser(function(user, done) {
var userData = {
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
displayName: user.displayName,
username: user.username,
email: user.email,
profileImageURL: user.profileImageURL,
roles: user.roles,
additionalProvidersData: user.additionalProvidersData
};
done(null, userData);
});
// Deserialize sessions
passport.deserializeUser(function(user, done) {
done(null, user);
});
// Initialize strategies
config.utils.getGlobbedPaths(path.join(__dirname, './strategies/**/*.js')).forEach(function(strategy) {
require(path.resolve(strategy))(config);
});
// Add passport's middleware
app.use(passport.initialize());
app.use(passport.session());
};
Is here the problem or should modify something else?. I believe that has something to with this because it also I have the problem that when I reload the session ends and I have to log in again.
Like the commnet above. Sean.js use Redis to store sessions, So you need to intall it first and then run it and the session will be available on req.user

Resources