This is my first time doing user authentication and I think I'm nearly complete. I'm using Express with Passport.js and MongoDB/Mongoose. When I run my gulp file everything loads correctly but when I attempt to login with my credentials I'm constantly redirected to the failure page (home screen). Here's my code:
// Initialize
const bodyParser = require('body-parser')
const path = require('path')
const bcrypt = require('bcryptjs')
const express = require('express')
const session = require('express-session')
const cookieParser = require('cookie-parser')
const passport = require('passport'),
LocalStrategy = require('passport-local').Strategy
let userData = require('./models/user.js')
const app = express()
// Configuration
app.use(cookieParser())
app.use(session({ secret: 'ThisIsTheSessionSecret', resave: true, saveUninitialized: true }))
app.use(passport.initialize())
app.use(passport.session())
app.use(express.static(path.join(__dirname, '../../docs')))
app.use(express.static(path.join(__dirname, '../../src')))
app.set('port', process.env.PORT || 8080);
app.set('view engine', 'pug')
app.set('views', 'src/pages')
let urlencodedParser = bodyParser.urlencoded({ extended: false })
// Configure authentication and sessions
passport.use(new LocalStrategy(
function(username, password, done) {
user.findOne({ username: username }, function(err, user) {
if (err) { console.log(err) }
if (!user) {
return done(null, false, { message: 'Incorrect username.' })
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' })
}
return done(null, user)
})
}
));
// Session
passport.serializeUser(function(user, done) {
done(null, user.id)
});
passport.deserializeUser(function(id, done) {
user.findById(id, function(err, user) {
done(err, user)
})
})
app.listen(app.get('port'))
console.log('Listening on port: ' + app.get('port'))
// Initialize database (local or remote?)
const MongoClient = require('mongodb').MongoClient,
mongoose = require('mongoose'),
assert = require('assert')
const database = 'mongodb://localhost:27017/database'
mongoose.connect(database)
let db = mongoose.connection
db.on('error', console.error.bind(console, 'connection error'))
db.once('open', function() {
// let testUser = new userData.UserModel({ username: 'PodcatchUser', password: 'RIPHarambe' })
// console.log(testUser.username)
})
// Routing
app.get('/', function(req, res) {
console.log('Welcome!')
res.render('index')
})
// Registration
app.post('/signup', urlencodedParser, function(req, res) {
let username = req.body.username,
password = req.body.password,
salt = bcrypt.genSaltSync(10),
hash = bcrypt.hashSync(password, salt)
let user = new userData.UserModel({
username: username,
password: hash
})
console.log(user.username)
console.log(user.password)
user.save(function(err) {
if (err) {
return console.log(err, user)
} else {
console.log('User has been saved to the database successfully')
}
})
res.end()
})
// Login
app.post('/login', passport.authenticate('local', { successRedirect: '/success',
failureRedirect: '/' }), function(req, res) {
res.end()
})
and the code in the userData module:
const mongoose = require('mongoose')
let UserSchema = mongoose.Schema({
username: String,
password: String
})
let UserModel = mongoose.model('UserModel', UserSchema)
module.exports.UserSchema = UserSchema
module.exports.UserModel = UserModel
I'm really confused as to what's happening. Mongo saves the credentials and hashed password to the database, I create a session and have a cookie parser but it doesn't authenticate. Help would be greatly appreciated :)
Related
I have an issue with persisting sessions in the DB.
I tried to store the session in MemoryStore to track the session's content, and the outupt is as expected, everything else works as expected.
But with MongoStore , the sessions collection gets createad in the DB and
everything else works as expected, but the session is not stored in the DB !!
Here is the code: ( i'm using Postman to send requests)
index.js -->
const express = require('express');
require('./db');
const passport = require('passport');
const session = require('express-session');
const cookieParser = require('cookie-parser');
const MongoStore = require('connect-mongo');
const authRouter = require('./routes/auth');
const productsRouter = require('./routes/routing');
require('./routes/passport-local');
// const memoryStore = new session.MemoryStore();
const app = express();
const PORT = 3000;
app.listen(PORT, () => console.log(`Server running on Port ${PORT}`));
app.use(express.json());
app.use(cookieParser());
app.use(
session({
secret: 'ALKSDFLKSDLFMK',
resave: false,
saveUninitialized: false,
sotre: MongoStore.create({
mongoUrl: 'mongodb://localhost/express_tuto',
}),
// store: memoryStore,
})
);
app.use(passport.initialize());
app.use(passport.session());
// app.use((req, res, next) => {
// console.log(memoryStore);
// console.log(req.user);
// next();
// });
app.use('/auth', authRouter);
app.use('/products', productsRouter);
./routes/auth.js -->
const { Router } = require('express');
const passport = require('passport');
const router = Router();
router.post(
'/login',
passport.authenticate('local'),
(req, res) => {
console.log(req.session);
console.log(req.user);
console.log(`sessionID: ${req.sessionID}`);
res.send('Logged in');
}
);
module.exports = router;
./routes/passport-local.js -->
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');
const User = require('../db/schemas/User');
const passport = require('passport');
passport.serializeUser((user, done) => {
console.log('serializing');
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
console.log('deserializing');
try {
const user = await User.findById(id);
if (user) {
done(null, user);
}
} catch (err) {
console.log(err);
done(err, null);
}
});
passport.use(
new LocalStrategy(
{ usernameField: 'email' },
async (email, password, done) => {
const user = await User.findOne({ email });
if (!user) {
console.log('no matching email!');
done(null, false);
}
try {
if (await bcrypt.compare(password, user.password)) {
console.log('user found');
done(null, user);
} else {
console.log('password incorrect');
done(null, false);
}
} catch (err) {
return done(err);
}
}
)
);
I am expecting to see the session stored in the DB given that everything ELSE works as expected!
My bad, there is a typo in the session store option.
sotre: MongoStore.create...//--> store:
Can someone explain to me the reason why LocalStrategy is not being called when I'm trying to login?
Basically nothing happens when I login it just refreshes the page.
require("dotenv").config()
const ejs = require("ejs");
const express = require("express")
const session = require('express-session');
const mongoose = require('mongoose')
const passport = require("passport")
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require("bcrypt")
const flash = require("express-flash")
//const User = require("./users")
//creating application
const app = express()
//const initializePassport = require("./passportconfig")
//connection to MongoDB with mongoose
main().catch(err => console.log(err));
async function main() { await mongoose.connect('mongodb://localhost:27017/passportDB') }
const userSchema = new mongoose.Schema({
name: String,
password: String,
userEmail: String
})
const User = mongoose.model("User", userSchema);
//middleware
//connecting to ejs
app.set("view engine", "ejs")
//connecting error text giver
app.use(flash())
//setup session
app.use(session({
secret: String(process.env.SECRET),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 * 24 * 7 }
}))
//connecting to body-parser
app.use(express.urlencoded({ extended: false }))
//passport.js
app.use(passport.initialize())
app.use(passport.session())
//serialize and deserialize session
passport.serializeUser(function (user, done) {
console.log('Deserialize user called.');
// #ts-ignore
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
console.log('Deserialize user called.');
User.findById(id, function (err, user) {
done(err, user);
});
});
//initializePassport(passport)
passport.use(new LocalStrategy((email, password, done) => {
User.findOne({ userEmail: email }, async function (err, user) {
if (err) {return done(err)}
if (!user) { return done(null, false, { message: 'No user with that email' }) }
if (await bcrypt.compare(password, user.password) == false) { return done(null, false, { message: 'No user with that email' }) }
return done(null, user);
});
}))
function isLoggedIn(req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect("/login");
}
app.get("/", isLoggedIn, (req, res) => {
res.render('index.ejs', { name: "Kyle" })
})
app.get("/login", (req, res) => {
res.render('login.ejs')
})
app.get("/register", (req, res) => {
res.render('register.ejs')
})
app.post("/register", async (req, res) => {
//10 is the value to generate the salt higher = more salted
try {
const hashedPassword = await bcrypt.hash(req.body.password, 10)
const user = new User({
name: req.body.name,
password: hashedPassword,
email: req.body.email
})
await user.save()
console.log(user)
res.redirect('login')
} catch {
res.redirect('register')
}
})
app.post("/login", passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}))
app.listen(3000, ()=> {
console.log("listening on port 3000")
})
Is there something I am missing?
The app.post for register is working all the data is entering the database it's simply for the login that's not working and it's because passport.authenticate is not working for the login post
I am using node.js express with sequelize and database postgreSql.the problem is in passport login failureRedirect works properly sucessRedirect does not redirect to the page that I want. It still loading and not responding anything and does not come any error.
when I submit login it will check for errors if errors it will work perfectly in failureRedirect but in Success it does not work like page has loading only not goes to the destination page and if I stop the project and restart the project it will be in destination page!! i dont know what is the problem help me.
mainController.js
const express = require("express");
const sessions = require("express-session");
require("../model/MasterUser.model");
const passport = require("passport");
var session = sessions;
const router = express.Router();
router.get("/dashboard", (req, res) => {
res.render('dashboard');
});
router.get("/login", (req, res) => {
res.render("login", { layout: "login.hbs" });
});
router.post(
"/login",
passport.authenticate("local", {
successRedirect: "/main/dashboard",
failureRedirect: "/main/login",
failureFlash: true,
})
);
module.exports = router;
passport.js
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcryptjs");
const sequelize = require("sequelize");
const masterUser = require("../model/MasterUser.model");
module.exports = function (passport) {
passport.use(
new LocalStrategy(
{ usernameField: "user_name" },
(user_name, password, done) => {
// Match user
masterUser.findOne({ where: { user_name: user_name } }).then((user) => {
if (!user) {
return done(null, false, {
message: "This username is not registered",
});
}
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Password incorrect" });
}
});
});
}
)
);
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
masterUser.findByPk(id, (err, user) => {
done(err, user);
});
});
};
index.js
const express = require("express");
const Handlebars = require("handlebars");
var flash = require("connect-flash");
const app = express();
const path = require("path");
const bodyparser = require("body-parser");
const expressHandlebars = require("express-handlebars");
const passport = require("passport");
const sessions = require("express-session");
var session = sessions;
const MainController = require("./controllers/MainController");
const db = require("./config/database");
//test db
db.authenticate()
.then(() => console.log("Database Connected..."))
.catch((err) => console.log("error" + err));
//for security purpose
const cors = require("cors");
app.use(
cors()
);
//Passport Config
require("./config/passport")(passport);
app.use(cookieParser());
//use body parser
app.use(bodyparser.json());
app.use(bodyparser.urlencoded({ extended: true }));
const {
allowInsecurePrototypeAccess,
} = require("#handlebars/allow-prototype-access");
app.use(
bodyparser.urlencoded({
urlencoded: true,
})
);
app.use(
sessions({
secret: "secret_key",
resave: false,
saveUninitialized: true,
cookie: { maxAge: 60000 },
})
);
// use flash for show messages
app.use(flash());
// Passport middleware
app.use(passport.initialize());
app.use(passport.session());
//flash messages
app.use((req, res, next) => {
res.locals.success_msg = req.flash("success_msg");
res.locals.error_msg = req.flash("error_msg");
res.locals.error = req.flash("error");
next();
});
//setting up view Engine
app.set("views", path.join(__dirname, "/views"));
//using the hbs
app.engine(
"hbs",
expressHandlebars({
extname: "hbs",
defaultLayout: "default",
layoutsDir: __dirname + "/views/layouts",
handlebars: allowInsecurePrototypeAccess(Handlebars),
})
);
app.set("view engine", "hbs");
//route for Main
app.use("/main", MainController);
//default
app.get("/", (req, res) => {
res.render("login");
});
app.listen(3000, () => {
console.log("App listening on port 3000!");
});
the problem has been solved guys I made done wrong code in deserializeUser.
passport.js before
passport.deserializeUser((id, done) => {
masterUser.findByPk(id, (err, user) => {
done(err, user);
});
});
};
passport.js after
passport.deserializeUser(function (id, done) {
masterUser.findOne({ where: { id: id } }).then((user) => {
done(null, user);
});
});
the problem is for sequelize get the user data is different so now its worked for me.this is useful for who using express with sequelize and passport with postgresql
I am currently creating a small user authentication app using Node + Express + Passport. When the user logs in, they are rerouted automatically to the index page "/" and a session should be established with passports authentication. For some reason when trying to console.log(req.user), it is returning "undefined".
The authentication with passport seems to be working properly with the post route
app.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
})
But the session is not being established with the user model. I'd like to eventually store the userId in the session. Here is a look at my current set up with user model and passport implementation on the server file.
const mongoose = require("mongoose");
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = mongoose.Schema({
username: String,
email: String,
password: String
});
userSchema.plugin(passportLocalMongoose);
const user = mongoose.model("User", userSchema);
module.exports = user;
-----------------------------------------------------------------------------------------
const express = require("express"),
mongoose = require("mongoose"),
bodyParser = require("body-parser"),
session = require("express-session"),
User = require("./models/user"),
passport = require('passport'),
LocalStragety = require('passport-local'),
app = express();
mongoose.connect("mongodb://localhost/shopping_cart_app", { useNewUrlParser: true })
.then(console.log("MongoDB Connected"))
.catch(err => console.log(err));
app.set("view engine", "ejs");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/views'));
app.use(session({
secret: "secret",
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStragety(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
})
I've tried looking into Passports config a bit more but on the documentation provided, it states that once passport.authenticate runs, a session with the user is established. Any tips would be greatly appreciate.
Thanks
I know this may seem simple, but have you tried req.body.user?
The req.body contains the data submitted by the user. The documentation suggest that you use a body parser to populate the information because it's undefined by default. However, instead of using the app object I use express router without parsing.
const express = require("express");
const router = express.Router();
router.post("/login", passport.authenticate("local", {
successRedirect: "/home",
failureRedirect: "/login"
}), (req, res) => {
console.log(req.body.user);
})
for more information: req.body
Try this one, In my project, it is working.
LocalStrategy
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var admins = mongoose.model('admins');
var bCrypt = require('bcrypt-nodejs');
var flash = require('connect-flash');
var moment = require('moment');
// User
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(obj, done) {
console.log("deserializing " + obj);
done(null, obj);
});
passport.use('adminlogin',new LocalStrategy(
function(username, password, done) {
admins.findOne({ 'email' : username },
function(err, user) {
//console.log(username);
if (err)
return done(err);
if (!user){
//console.log('Username '+username+' does not Exist. Pleasr try again.');
return done(null, false, { message: 'Incorrect Username/Password. Please try again.' });
}
if (!isValidPasswordAdmin(user, password)){
//console.log('Invalid Password');
return done(null, false, { message: 'Incorrect Password. Please try again.' });
}
return done(null, user);
}
);
})
);
var isValidPassword = function(user, app_pin){
return bCrypt.compareSync(app_pin, user.app_pin);
}
var isValidPasswordAdmin = function(user, password){
return bCrypt.compareSync(password, user.password);
}
module.exports = passport;
Login Route
router.post('/login', function (req, res, next) {
admins.find({}, function (err, user) {
if (err) {
console.log('internal database error');
req.flash('error', 'Database Error');
res.redirect('/admins');
} else {
passport.authenticate('adminlogin', function (err, user, info) {
if (err) {
console.log(err);
req.flash('error', 'Database Error');
res.redirect('/admins');
} else if (!user) {
req.flash('error', info.message);
res.redirect('/admins');
} else {
req.logIn(user, function (err) {
if (err) {
req.flash('error', 'Database Error');
res.redirect('/admins');
} else {
res.redirect('/admins/home');
}
});
}
})(req, res, next);
}
});
});
The code
app.js:
var express = require('express');
var session = require('express-session');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongoStore = require('connect-mongo')(session);
var mongoose = require('mongoose');
var passport = require('passport');
var config = require('./config');
var routes = require('./routes');
var mongodb = mongoose.connect(config.mongodb);
var app = express();
// view engine setup
app.set('views', config.root + '/views');
app.set('view engine', 'jade');
app.engine('html', require('ejs').renderFile);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(config.root + '/public'));
app.use(session({
name: 'myCookie',
secret: 'tehSecret',
resave: true,
saveUninitialized: true,
unset: 'destroy',
store: new mongoStore({
db: mongodb.connection.db,
collection: 'sessions'
})
}));
app.use(passport.initialize());
app.use(passport.session());
app.use('/', routes);
app.set('port', config.port);
var server = app.listen(app.get('port'), function() {
if (config.debug) {
debug('Express server listening on port ' + server.address().port);
}
});
routes.js:
var express = require('express');
var router = express.Router();
var config = require('../config');
var userController = require('../controllers/user');
var authController = require('../controllers/auth');
router.get('/', function(req, res) {
res.render('index', {
title: config.app.name
});
});
router.route('/users')
.post(userController.postUsers)
.get(authController.isAuthenticated, userController.getUsers);
router.get('/signout', userController.signout);
module.exports = router;
models/user.js:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true
},
password: {
type: String,
required: true
}
});
// Execute before each user.save() call
UserSchema.pre('save', function(callback) {
var user = this;
// Break out if the password hasn't changed
if (!user.isModified('password')) return callback();
// Password changed so we need to hash it
bcrypt.genSalt(5, function(err, salt) {
if (err) return callback(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return callback(err);
user.password = hash;
callback();
});
});
});
UserSchema.methods.verifyPassword = function(password, cb) {
bcrypt.compare(password, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
// Export the Mongoose model
module.exports = mongoose.model('User', UserSchema);
controllers/user.js:
var config = require('../config');
var User = require('../models/user');
exports.postUsers = function(req, res) {
if (config.debug)
console.log("user.postUsers()");
var user = new User({
username: req.body.username,
password: req.body.password
});
user.save(function(err) {
if (err)
return res.send(err);
if (config.debug)
console.log("saved");
res.json({
message: 'New user created!'
});
});
};
exports.getUsers = function(req, res) {
if (config.debug)
console.log("user.getUsers()");
User.find(function(err, users) {
if (err)
return res.send(err);
if (config.debug)
console.log("users", users);
res.json(users);
});
};
exports.signout = function(req, res) {
if (config.debug)
console.log("user.signout()");
res.clearCookie('myCookie');
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
};
controllers/auth.js:
var passport = require('passport');
var BasicStrategy = require('passport-http').BasicStrategy;
var config = require('../config');
var User = require('../models/user');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new BasicStrategy(
function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(err);
}
// No user found with that username
if (!user) {
return done(null, false);
}
// Make sure the password is correct
user.verifyPassword(password, function(err, isMatch) {
if (err) {
return done(err);
}
// Password did not match
if (!isMatch) {
return done(null, false);
}
// Success
return done(null, user);
});
});
}
));
exports.isAuthenticated = passport.authenticate('basic', {
session: false
});
The problem
/signout route does not end the current session. In the req.session.destroy callback the req.session is undefined, yet a new GET request to /users acts like the session is valid.
Can someone help clear this problem out?
If, like me, you came here as a result of question title rather than full details- the answer is req.session.destroy(). I think the logout function is particular to passport.js and will not work if you are using standard express-session.
Solution
controllers/user.js:
exports.signout = function(req, res) {
if (config.debug)
console.log("user.signout()");
req.logout();
res.send(401);
};
Btw. don't mind the session(s) still being in DB immediately after the logout. Mongod checks and clears those out after 60 s.
in sign out api without using req.session.destroy() try req.logout();. I hope it will work.
In my case the server-side code was fine. It was the client-side code where I wasn't including the withCredentials parameter when making the http request.
Below is the correct working code.
// server side (nodejs)
authRouter.post("/logout",
passport.session(),
checkAuthenticationHandler,
async (req, res, next) => {
req.logOut(err => {
if (err) next(err)
res.status(http.statusCodes.NO_CONTENT).end()
})
})
// client side (reactjs)
export const logout = async () => {
const _response = await axios({
method: 'post',
url: `${authApi}/auth/logout`,
withCredentials: true
})
}