Passport.js - Error: failed to serialize user into session - node.js

I got a problem with the Passport.js module and Express.js.
This is my code and I just want to use a hardcoded login for the first try.
I always get the message:
I searched a lot and found some posts in stackoverflow but I didnt get the failure.
Error: failed to serialize user into session
at pass (c:\Development\private\aortmann\bootstrap_blog\node_modules\passport\lib\passport\index.js:275:19)
My code looks like this.
'use strict';
var express = require('express');
var path = require('path');
var fs = require('fs');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var nodemailer = require('nodemailer');
var app = express();
module.exports = function setupBlog(mailTransport, database){
var config = JSON.parse(fs.readFileSync('./blog.config'));
app.set('view options', {layout: false});
app.use(express.static(path.join(__dirname, '../', 'resources', 'html')));
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({ secret: 'secret' }));
app.use(passport.initialize());
app.use(passport.session());
app.get('/blog/:blogTitle', function(req, res) {
var blogTitle = req.params.blogTitle;
if(blogTitle === 'newest'){
database.getLatestBlogPost(function(post) {
res.send(post);
});
} else {
database.getBlogPostByTitle(blogTitle, function(blogPost) {
res.send(blogPost);
});
}
});
passport.use(new LocalStrategy(function(username, password, done) {
// database.login(username, password, done);
if (username === 'admin' && password === 'admin') {
console.log('in');
done(null, { username: username });
} else {
done(null, false);
}
}));
app.post('/login', passport.authenticate('local', {
successRedirect: '/accessed',
failureRedirect: '/access'
}));
app.listen(8080);
console.log('Blog is running on port 8080');
}();
Thanks.

It looks like you didn't implement passport.serializeUser and passport.deserializeUser. Try adding this:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});

If you decide not to use sessions, you could set the session to false
app.post('/login', passport.authenticate('local', {
successRedirect: '/accessed',
failureRedirect: '/access',
session: false
}));

Sounds like you missed a part of the passportjs setup, specifically these two methods:
passport.serializeUser(function(user, done) {
done(null, user._id);
// if you use Model.id as your idAttribute maybe you'd want
// done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
I added the bit about ._id vs. .id but this snippet is from the Configure Section of docs, give that another read and good luck :)

Here an working but still lazy way to use sessions and still "serialisize" the values.
var user_cache = {};
passport.serializeUser(function(user, next) {
let id = user._id;
user_cache[id] = user;
next(null, id);
});
passport.deserializeUser(function(id, next) {
next(null, user_cache[id]);
});
in case or weird errors just ask yourself: "Do I rlly set '_id' in my user object?" - in most cases you dont. So use a proper attribute as key.

Make sure you have used async and await when getting user data.
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
const USER = await User.findById(id);
done(null, USER);
});
passport.use(
new GoogleStrategy(
{
// options for google strategy
clientID: keys.google.clientID,
clientSecret: keys.google.clientSecret,
callbackURL: "/auth/google/redirect",
},
async (accessToken, refreshToken, profile, done) => {
// passport callback function
// check if user already exist in our db
const oldUser = await User.findOne({ googleId: profile.id });
if (oldUser) {
return done(null, oldUser);
} else {
const newUser = await new User({
username: profile.displayName,
googleId: profile.id,
}).save();
return done(null, newUser);
}
}
)
);

use this code into your server.js file where u can make a get or post request.
app.get('/auth/facebook/callback',passport.authenticate('facebook',{
successRedirect:'/profile',
failureRdirect:'/',
session: false
}))

Using Promise with serializeUser & deserializeUser:
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
// console.log(`id: ${id}`);
User.findById(id)
.then((user) => {
done(null, user);
})
.catch((error) => {
console.log(`Error: ${error}`);
});
});
Please see my github repo for a full code example how to solve this issue.

You missed this code:
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
For details, check the session part in http://www.passportjs.org/docs/configure/

Another prevalent reason for this as I discovered today is that if you are using the standard packages such as passport,passport-local,passport-local-mongoose and express-session and u are already in a session that is you were logging in and then you terminate the server and restart again, you will be in the same session while logging in where the browser keeps loading with no response and you get this error of "failed to serialize user into the session".

Since "user" is returned as an array of objects, try this:
passport.serializeUser(function(user, cb) {
process.nextTick(function() {
return cb(null, user[0].id);
});
});

in passport.use('local-login'...)/ or /('local-singup'...)
if err you have to return "false"
err {return done(null, req.flash('megsign', 'Username already exists #!#'));}
true {return done(null, false, req.flash('megsign', 'Username already exists #!#'));}

Related

Cannot read property 'use' of undefined, in the passport .js file

I created a folder config and i created passport.js file inside it, that holds all the configuration passport module when i tried to run my code i got this error TypeError: Cannot read property 'use' of undefined referring to this line of code passport.use(new LocalStrategy(function(username, password, done) {
passport.js file :
var express = require('express');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../server/model/userModel');
const bcrypt = require('bcrypt');
var passport = require('passport');
module.exports = function(passport) {
// Local Strategy
passport.use(new LocalStrategy(function(username, password, done) {
// Match Username
let query = { username: username };
User.findOne(query, function(err, user) {
/*
if (err) throw err;
if (!user) {
return done(null, false, { message: 'No user found' });
}
*/
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
// Match 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);
});
});
}
You use two different references for the same name.
var passport = require('passport');
Here, you defined the passport variable.
module.exports = function(passport) {
Here, you define the parameter name as passport. Using passport in this context will refer to the parameter
passport.use(...
Calling on the parameter's use method instead of the passport.use method from the defined variable. Consider changing the function parameter name to something other than passport, maybe _passport?
module.exports = function(passport) {
//to
module.exports = function(_passport) {
Have you initialised:
app.use(passport.initialize());
app.use(passport.session());
To use Passport in an Express or Connect-based application, configure it with the required passport.initialize() middleware. If your application uses persistent login sessions (recommended, but not required), passport.session() middleware must also be used.
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); }
if (!user.verifyPassword(password)) { return done(null, false); }
return done(null, user);
});
}
));
https://github.com/jaredhanson/passport

Github oAuth with PassportJS not authenticated

I'm trying to use passportJS to authenticate with the github-strategy. I've previously used passportJS with success, but not with the github-strategy, though I don't figure it should be much different. /viewData is the route in which I'm testing for authentication.
The strategy authenticates successfully and puts the data in the DB and when I serialize and deserialize the user data is in fact there. The problem comes when I try to check in any route after if the req.isAuthenticated() or even if req.session.passport.user exists and it returns false and undefined respectively. The weird thing the same check in app.js after I've redirected through /testAuth the authentication logs correctly with the true and id#. So I feel like its some issue with express-session or the passport integration with that that's breaking it, but I can't find it. I've been on this for a long time so hopefully someone can help me.
app.use(cookieParser('test'));
app.use(session({
secret: 'test',
resave: false,
saveUninitialized: true
}));
//pasport setup
app.use(passport.initialize());
app.use(passport.session());
// Passport init
passport.use(new GithubStrategy({
clientID: config.github.clientID,
clientSecret: config.github.clientSecret,
callbackURL: config.github.callbackURL },
function(accessToken, refreshToken, profile, done) {
User.findOne({ oauthID: profile.id }, function(err, user) {
if(err) {
console.log(err); // handle errors!
}
if (!err && user !== null) {
done(null, user);
} else {
user = new User({
oauthID: profile.id,
name: profile.displayName,
created: Date.now()
});
user.save(function(err) {
if(err) {
console.log(err); // handle errors!
} else {
console.log("saving user ...");
done(null, user);
}
});
}
});
}
));
// test authentication
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
console.log("Not Authenticated", req.user);
res.redirect('/');
}
app.get('/testAuth', ensureAuthenticated, function(req, res){
User.findById(req.session.passport.user, function(err, user) {
if(err) {
console.log(err); // handle errors
} else {
res.redirect('/viewData');
}
});
});
//auth pages
app.get('/auth/github',
passport.authenticate('github'),
function(req, res){});
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/' }),
function(req, res) {
res.redirect('/testAuth');
});
// serialize and deserialize for session
passport.serializeUser(function(user, done) {
console.log('serializeUser: ' + user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user){
console.log('deserializeUser: ' + user);
if(!err) done(null, user);
else done(err, null);
});
});
So this is a weird one, but I was fixing something else that was weird and in doing so solved this problem. I just moved my
app.use('/', index);
app.use('/viewData', viewData);
To below all the passport code and it works now.

Passport req.isAuthenticated() doesn't work

I'm using express-session and passport to authenticate.
Here's my module :
(updated)
passport.use(new LocalStrategy(
function(username, password, done) {
LamAdmin.findOne({ email: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Incorrect username' }); }
if (!comparePassword(user.password, password)) { return done(null, false, { message: 'Incorrect password' }); }
return done(null, user);
});
}
));
router.post('/login', passport.authenticate('local',{
successRedirect: '/p/dashboard',
failureRedirect: '/p',
failureFlash: true
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
LamAdmin.findById(id, function (err, user) {
done(err, user);
});
});
function checkAuth(req,res,next) {
if (req.isAuthenticated()) {
next();
} else {
res.redirect('/p');
}
}
// //DOES NOT WORK WITH CHECKAUTH MIDDLEWARE
// router.get('/dashboard',(req,res,next)=> {
// res.render('dashboard');
// });
router.get('/dashboard',checkAuth,(req,res,next)=> {
res.render('dashboard');
});
checkAuth is my middleware and idk why it always returns false.
This is passport and session configs in app.js :
//sessions
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true,
cookie: { secure: false } // Remember to set this
}));
//passport
app.use(passport.initialize());
app.use(passport.session());
Is there anything wrong with my checkAuth middleware ?
Or my redirect to Dashboard view?
Thanks in advance!
When you call passport.authenticate() method, it uses a strategy to authenticate the user. If you have the user information stored in a local DB, then you can use the Local Strategy that will use username and password to authenticate. So in your app.js add the following:
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
/* Change this to fetch user info from your db
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!comparePasswords(user.password, password)) { return done(null, false); }
return done(null, user);
});
*/
}
));
When the user is authenticated, serializeUser() method is called with the user object returned from the above method passed as an argument and express appends the value returned to the current user session in req.session.passport.user
passport.serializeUser(function(user, done) {
done(null, user.id);
});
On each subsequent request, when isAuthenticated() method is called, passport checks if some value is present in req.session.passport.user. If user information is present, it calls the deserializeUser() method which appends the user information to the request and can be accessed via req.user
passport.deserializeUser(function(id, done) {
// Replace it with your db
User.findById(id, function (err, user) {
done(err, user);
});
});

PassportJS Local Strategy not working Withh MongoDB

I'm trying to get a handle on user authentication with PassportJS, and I cannot get it to work my database setup.
I'm using MongoDB without Mongoose, and I can't get the LocalStrategy module to work.
Hopefully my database queries aren't too cumbersome to read.
Local Strategy:
passport.use(new LocalStrategy(
function(username, password, done) {
//Fire up database
mongo.connect("mongodb://localhost:27017/formulas", function(e, db) {
if (e) {return next(e);}
var col = db.collection("users");
//Do a database query to find a record by username
col.findOne({"username": username}, function(err, user){
if (err) { return done(err);}
if(!user) {
return done(null, false, { message: "Please check your log in credentials." });
}
//if it exists call done() object with user information
bcrypt.compare(password, user.password, function(err, res){
if (err) throw err;
if (res == true) {
return done(null, {username: username, password: password});
} else {
return done(null, false, { message: "Invalid password."});
}
});
});
});
}));
I call passport.authenticate() like this:
router.post('/login',
passport.authenticate('local', {successRedirect:'/', failureRedirect:'/about',failureFlash: false}),
function(req, res){
console.log(req.body);
console.log(req.user);
console.log("The user was logged");
});
SerializeUser and deserializeUser look like this:
passport.serializeUser(function(user, done) {
done(null, user.username);
});
passport.deserializeUser(function(id, done) {
mongo.connect("mongodb://localhost:27017/formulas", function(e, db){
if (e) {return next(e);}
var col = db.collection("users");
col.findOne({"username": id}, function(err, user){
done(err, {"username": id});
});
});
});
When I call app.post(/login) I'm taken directly to /about and nothing is logged to the console so I'm not quite sure what's going wrong.
Advice on what to fix or how to troubleshoot is greatly appreciated.
Firstly when u serialize user object , then in deserialize also whole user object must be passed.
Consider the example below.
For routes :
router.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
req.session.user = req.user;
return res.redirect('/home');
});
})(req, res, next);
});
For passport.js , place the localstrategy and passport in same folder
var passport = require('passport'),
session = require('express-session');
var local = require('./localstrategy.js');
module.exports = function (app) {
app.use(session({
secret: 'Site visit',
resave: true,
saveUninitialized: true,
cookie: { secure: false }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
local();
};
For local strategy :
'use strict';
var passport = require('passport'),
local = require('passport-local').Strategy;
var user;
// path where the db.js is placed
var db = require('./../db.js');
var ObjectId = db.getObjectID();
var bcrypt = require('bcrypt');
module.exports = function(){
passport.use(new local({
usernameField : 'username',
passwordField : 'password'
}, function(username, password, done){
var collection = db.getDb().collection('users');
collection.findOne({
username: username,
}, function (err, result) {
if(result == null){
cb(null, false);
}else {
bcrypt.compare(password, result.password, function (err, passRes) {
if (passRes == true) {
user = user;
done(err, user);
}else{
done(null, false, { message : 'Invalid Password'});
}
});
}
});
}));
};

facebook login error - using MEAN

I am new to MEAN. I've been trying to create a 'facebook' login for my new MEAN Application. I am making use of strategy from: "passport-facebook".
The code from my User.js file, where i've created a new Schema for facebook users is as follows:
var mongoose = require('mongoose');
var fbuserSchema = mongoose.Schema({
id: String,
token: String,
email: String,
name: String
});
var fbUser = mongoose.model('fbUser', fbuserSchema);
My passport.js file has something like this:
var fbUser = mongoose.model('fbUser'),
FacebookStrategy = require('passport-facebook').Strategy;
module.exports = function(){
passport.serializeUser(function (user, done) {
if (user) {
done(null, user.id);
}
});
passport.deserializeUser(function (id, done) {
User.findOne({_id: id }).exec(function (err, user) {
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
});
passport.use(new FacebookStrategy({
'clientID' : 'xxxxxxxxxxxxxxx',
'clientSecret' : 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
'callbackUrl' : 'http://localhost:3030/auth/facebook/callback'
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function(){
fbUser.findOne({'id':profile.id}, function(err, user){
if(err){
return done(err);
}
if(user){
return done(null, user);
}
else{
var newUser = new fbUser();
newUser.id = profile.id;
newUser.token = accessToken;
newUser.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.email = profiel.emails[0].value;
newUser.save(function(err){
if(err){
throw err;
}
return done(null, newUser);
});
}
});
});
})
)
}
and my routes.js has:
var auth = require('./auth'),
users = require('../controllers/users'),
mongoose = require('mongoose'),
User = mongoose.model('User'),
fbUSer = mongoose.model('fbUser'),
passport = require('passport');
module.exports= function(app) {
app.get('/api/users', auth.requiresRole('admin'), users.getUsers);
app.post('/api/users', users.createUser);
app.put('/api/users', users.updateUser);
app.get('/partials/*', function (req, res) {
res.render('../../public/app/' + req.params[0]);
});
app.post('/login', auth.authenticate);
app.get('/auth/facebook', passport.authenticate('facebook'));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/',
failureRedirect: '/login' }));
app.post('/logout', function(req,res){
req.logout();
res.end();
});
app.get('*', function (req, res) {
res.render('index', {
bootstrappedUser: req.user
});
});
}
I have provided the:
a(href="/auth/facebook") Facebook
in my login.jade file.
When i click on this link though all i am getting is a error saying:
The parameter redirect_uri is required
Can someone please help me on this issue.
My facebook URL is as follows:
https://www.facebook.com/v2.2/dialog/oauth?response_type=code&redirect_uri=&client_id=xxxxxxxxxxxxxxxx
I am missing the "redirect_uri=" value in this. How do i get that?
MEAN already had an facebook support;
Go for facebook.js under config/strategies/
Change the facebook id and app secret according to the one you filled in the facebook website.
Thats it

Resources