Server returning "Error: Failed to serialize user into session" when attempting to use register user using Passport.js and AJAX - node.js

Server returning "Error: Failed to serialize user into session" when attempting to use register user using Passport.js and AJAX. The code works when not using AJAX from the registration page, but I want to have a popup that processes the registration too.
passport.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser((id, done) => {
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local.popsignup', new LocalStrategy({
usernameField: 'popUsername',
passwordField: 'popPassword',
passReqToCallback: true
}, (req, email, password, done) => {
User.findOne({'email':email}, (err, user) => {
if(err){
return done(err);
}
if(user){
return done(null, false, req.flash('error', 'Email already exists, please login.'))
}
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(password);
newUser.preferences = preferences;
newUser.save((err) => {
return done(null, newUser);
});
});
}));
user router
module.exports = (app, passport) => {
app.post('/popover', function(req, res, next) {
passport.authenticate('local.popsignup', function(err, user, info) {
if (user !== undefined) {loggedIn = true} else {loggedIn = false};
var errors = req.flash('error')
req.login(user, function(err) {
if (err) return next(err);
res.status(200).send({errors: errors, loggedIn: loggedIn});
});
})(req, res, next);
});
}
view ajax js
$.ajax({
url: "/popover",
type: "POST",
data: $('#popform-tag').serialize(),
success: function(data){
if(data.loggedIn == false) {
$("#email_alert").text(data.errors[0]).show();
} else {
$('#popover').modal('hide')
localStorage.setItem('preferences', 1);
}
//console.log(data);
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Status: " + textStatus);
console.log("Error: " + errorThrown);
}
})

Before adding ajax, send some parameters via PostMan to check if your API request-response is working as expected. If yes, only then use Ajax based on API type (post, put etc..)
Also instead of serialize() try using Ajax in this way -
var data = {
someKey: 'someValue'
}
$.ajax({
url: '/popover', // route path
type: 'POST',
data: JSON.stringify(data),
headers: {
'Content-Type': "application/json",
},
'success': function (data, textStatus, request) {
// do something
},
'error': function (err) {
// do some error handling
}
});

Related

Passport doesn't save the session

I have read a lot about this issue but any answer doesn't work for me. I am working with React, Express and Passport to manage my authenticate routine. The authentication routine is fine, and it makes the redirect that I want. But, when I refresh any route, it says me that I am not authenticate. It seems that Passport doesn't save the session. Here my code:
Server.js
const lisaApp = express();
lisaApp.use(bodyParser.json())
lisaApp.use(bodyParser.urlencoded({ extended: false }))
lisaApp.use(cookieParser())
lisaApp.use(session({
secret: config.secret,
resave: false,
saveUnitialized: false
}))
lisaApp.use(passport.initialize())
lisaApp.use(passport.session())
passport.use(auth.localStrategy);
passport.serializeUser(auth.serializeUser);
passport.deserializeUser(auth.deserializeUser);
lisaApp.post('/login', (req, res, next) => {
const validationResult = validateLoginForm(req.body);
if (!validationResult.success) {
return res.status(400).json({
success: false,
message: validationResult.message,
errors: validationResult.errors
});
}
return passport.authenticate('local', (err, userData) => {
if (err) {
if (err.response.statusText === 'Unauthorized') {
return res.status(400).json({
success: false,
message: 'The password is not right'
});
}
return res.status(500).json({
success: false,
message: 'Wrong data'
});
}
console.log('is authenticated?: ' + req.isAuthenticated()) // Here always is false
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
})(req, res, next);
});
// I am using this function as a middleware to check if the user is authenticated, always is false. No matter if I put right data in the login form
function ensureAuth (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.status(401).send({ error: 'not authenticated' })
}
auth/index.js(passport routine)
var LocalStrategy = require('passport-local').Strategy;
var LisaClient = require('pos_lisa-client');
var config = require('../config');
var ClientApi = LisaClient.createClient(config.lisaClient);
exports.localStrategy = new LocalStrategy({
usernameField: 'username',
passwordField: 'password',
session: false,
passReqToCallback: true
}, (req, username, password, done) => {
var authData = {
username,
password
}
// Here is a custom API, I pass the data that I need. This works fine
ClientApi.authenticate(authData, (err, token) => {
if (err) {
return done(err)
}
var token = token.data
ClientApi.getClient(username, (err, user) => {
if (err) {
return done(err)
}
user.token = token
return done(null, user)
})
})
})
exports.serializeUser = function (user, done) {
// The app never enters here
done(null, {
username: user.username,
token: user.token
})
}
exports.deserializeUser = function (user, done) {
// The app never enters here
ClientApi.getClient(user.username, (err, usr) => {
if (err) {
return done(null, err)
} else {
usr.token = user.token
done(null, usr)
}
})
}
Where I am wrong?
If you're using a custom authentication callback, you have to call req.logIn() to establish a session (or you can create one manually):
// Add this where you are console.log'ing `req.isAuthenticated()`
req.logIn(userData, function(err) {
if (err) return next(err);
console.log('is authenticated?: ' + req.isAuthenticated());
return res.json({
success: true,
token,
message: 'Successful Login',
user: userData.data
});
});
This is documented here (scroll down to "Custom Callback"):
Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.

PassportJS authentication

So, I have everything working but it is not showing it is an authenticate user even though it arrives at the proper places...
javascript code from the page to validate login
var UserManager = {
validateLogin : function (username, password) {
var userData = {
username: username,
password: password
}
return new Promise(function(resolve, reject) {
$.ajax({
url: "/musicplayer/users/api/login",
dataType: "json",
data: userData,
type: "POST",
success: function loginSuccess(result, status, xhr) {
resolve(null);
},
error: function loginError(xhr, status, result) {
reject(new Error(result));
},
});
});
}
}
function userLogin(){
UserManager.validateLogin($('#loginEmail').val(), $('#loginPassword').val()).then(function(response) {
window.location = '/musicplayer/library'
},
function(error){
$("#msgBox").messageBox({"messages" : error.message, "title" : "Warning", boxtype: 4 });
$("#msgBox").messageBox("show");
});
return false;
}
local.strategy.js
var passport = require('passport');
var localStrategy = require('passport-local').Strategy;
var userLibrary = require('../../classes/music/userlibrary.js');
module.exports = function () {
passport.use(new localStrategy(
{
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
//validating user here
var userManager = new userLibrary.UserManager();
userManager.login(username, password).then(
function (user){
done(null, user);
},
function (reason){
if (reason.err) {
done(err, false, info);
}
else {
done(null, false, {message: reason.message});
}
}
);
})
);
};
Router
/******* validate the user login ********/
usersRouter.post('/api/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
console.log("Login Failed", err.message + " - " + err.stack);
if (req.xhr){
res.status(500).send({ error: 'Internal Error' });
}
else {
next(err);
}
}
else if (!err && !user){
err = new Error();
err.message = info.message;
err.status = 401;
console.log("Invalid Data", err.message);
if (req.xhr){
res.status(401).send({ error: err.message });
}
else {
next(err);
}
}
else if (user){
console.log("Successful Login:", user);
res.status(200).send({message: "successful"});
}
}
)(req, res, next);
});
passport.js file which has my Middleware...
var passport = require("passport");
module.exports = function (app) {
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done){
done(null, user);
});
passport.deserializeUser(function(user, done){
done(null, user);
});
require('./strategies/local.strategy')();
app.all('/musicplayer/*', function (req, res, next){
// logged in
//need function for exceptions
if (req.user || req.url === '/musicplayer/users/api/login' || req.url === '/musicplayer/users/signin') {
next();
}
// not logged in
else {
// 401 Not Authorized
var err = new Error("Not Authorized");
err.status = 401;
next(err);
}
});
}
Userlibrary/UserManager
I am using promises to be able to utilize the creation of a library and to deal with sync versus async issues that I ran into early on...
var sqlite3 = require('sqlite3').verbose();
function User() {
this.email = "";
this.password = "";
this.userid = "";
};
function UserManager () {
this.user = new User();
};
UserManager.prototype.login = function (email, password) {
var db = new sqlite3.Database('./data/MusicPlayer.db');
params = {
$email: email,
$password: password
}
var self = this;
return new Promise(function(resolve, reject){
db.serialize(function () {
db.get("SELECT * FROM users WHERE email = $email and password = $password", params, function (err, row) {
db.close();
if (!err && row) {
//log in passed
self.user.userid = row.userid;
self.user.email = row.email;
self.user.password = row.password;
resolve(self.user);
}
else if (!err) {
//log in failed log event
reject({
err: err,
message: null
});
}
else {
//error happened through out an event to log the error
reject({
message : "Email and/or Password combination was not found",
err : null
});
}
});
});
});
};
module.exports = {
User : User,
UserManager : UserManager
}
Now, I have debugged this and it is for sure getting to "successful Login"
Returns to the browser with success, the browser says okay let me redirect you to the library page (which is really just a blank page). When it goes to my library page I get a 401 unauthorized.
So if I debug inside the middleware to ensure authentication. I look at req.user and it is undefined and I try req.isAuthenticated() it returns a false.
I think I must be missing something...
What I want is a global authentication saying hey is this person logged in. And then I will set up the route/route basis say okay do they have permission for this page or web service call.
Right now I am sticking with session for everything as it is not useful to me to learn web tokens at this point and time.
Any help would be appreciated... I been around and around on this looking at examples out there. But the examples I find are the "basic" examples no one calling a library to validate from database or they are not trying to set up the authorization globally but rather on a route by route basis.
Upon searching I found this article
https://github.com/jaredhanson/passport/issues/255
then I found this in documentation
app.get('/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); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
and that worked for me... I basically forgot to do the req.logIn method itself when using the custom callback.... I knew it was something simple... Hope this helps someone in the future.

Passport local authentication is not working

I am building app using MEAN STACK. I want to use passport-local authentication for my login form. But a the time of form submission i am getting POST http://localhost/login 404 (Not Found) please have a look of my code below This is my controller:
lyfee.controller('loginCtrl', ['$scope', '$http', function($scope, $http) {
$scope.user = {};
$scope.login = function() {
// var data = {User: $scope.user }
//console.log($scope.user);
console.log("login function call");
$http.post('/login', $scope.user);
console.log("login request send");
}
}]);
and this is my server.js :
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByusername(username, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
User.comparePassword(password, user.password, function(err, isMatch) {
if () throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));
app.post('/login',
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login',
failureFlash: true
}),
function(req, res) {
res.redirect('/');
});
in which file should i write getUserByusername and comparePassword function ? and what is mistake i am doing please correct it and give me some suggestion.
In your model suppose that User.js write functions like this:
/**
* Check the user's password
*/
dbSchema.methods.comparePassword = function(candidatePassword, cb) {
var status = this.password.localeCompare(candidatePassword.trim());
if (status != 0) {
return cb(err);
}
cb(null, true);
};
then use the function like this
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) throw err;
if (!user) {
return done(null, false, {
message: 'Unknown USER'
});
}
/**
* Check the user's password
*/
User.comparePassword(password, user.password, function(err, isMatch) {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {
message: 'Invalid password'
});
}
});
});
}));

Modify req.user or use req.account?

I'm developing a mean application with passport, and I'm running through this issue:
I have a LocalStrategy to log on the user based on the application database. I need, however to login the user simultaneously on another service with possible multiple accounts. The thing is, once I route to authorize these logins, and set the variables to req.account, I cannot access them in other routes. Note that I can get the data I want, I just want to access it from somewhere other than this route, like req.user. I will post some of my code to clarify the situation.
Local Login route
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user) {
if (err)
return next(err);
if (!user)
return res.status(400).json({status: 'Invalid Username'});
req.login(user, function (err) {
if (err)
return next(err);
res.status(200).json({status: 'User successfully authenticated'});
});
})(req, res, next);
});
Local login passport config
passport.use('local-login', new LocalStrategy(function (user, pswd, done) {
User.findOne({'username': user}, function (err, user) {
if (err)
return done(err);
if (!user || !user.validPassword(pswd))
return done(null, false);
return done(null, user);
});
}));
The other service passport config
passport.use('other-login', new OtherStrategy(function (docs, done) {
if (docs.length === 0)
return done(null, false);
var accounts = [];
var user, pswd, data;
var counter = docs.length;
for (var i = 0; i < docs.length; i++) {
user = docs[i]._id;
pswd = docs[i].password;
request.post(<serviceurl>, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: qs.stringify({
grant_type: 'password',
username: user,
password: pswd,
client_id: process.env.API_KEY
})
}, function (err, res, body) {
if (err)
return done(err);
data = JSON.parse(body);
data.username = docs[docs.length - counter]._id;
accounts.push(data);
counter--;
if (counter === 0)
return done(null, accounts);
});
}
}));
Other Service route
router.get('/otherservice', passport.authorize('other-login', {}) , function (req, res) {
console.log(req.account);
res.sendStatus(200);
});
Other Service authentication (from custom Strategy)
ServiceStrategy.prototype.authenticate = function (req) {
var self = this;
var id = req.user.master_id || req.user.id;
Service.find({master_id: id}, function (err, docs){
if (err)
return self.error(err);
function verified(err, data, info) {
if (err) { return self.error(err); }
if (!data) { return self.fail(info); }
self.success(data, info);
}
try {
if (self._passReqToCallback) {
self._verify(req, docs, verified);
} else {
self._verify(docs, verified);
}
} catch (ex) {
return self.error(ex);
}
});};
I found the solution! On the User Model, I added an accounts property to store the data returned on the authorization. Then, on the authorization route, I updated the user with this info, and saved. It wasn't that hard at all.
app.post('/api/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user) {
if (err)
return next(err);
if (!user)
return res.status(400).json({status: 'Invalid Username'});
req.login(user, function (err) {
if (err)
return next(err);
var id = req.user.master_id || req.user.id;
Service.findOne({master_id: id}, function (err, doc) {
if (doc == null)
res.status(200).json({
status: 'User successfully authenticated',
accounts: false
});
else
return next();
});
});
})(req, res, next);
}, passport.authorize('other-login', {}), function (req, res) {
var accounts = req.account;
var user = req.user;
user.accounts = accounts;
user.save(function (err, newUser) {
if (err)
throw err;
res.status(200).json({
status: 'User sucessfully authenticated',
accounts: true
});
})
});

req.session.passport and req.user empty, serializeUser and deserializeUser are never called

I'm using Express (v4.11.2) with Passport in order to support multiple providers (local, facebook, twitter and google) for access to the web app I'm building. As a backend I'm using mysql. For now I have two local strategies: local-signup and local-signin. The issue I'm experiencing is that the req.session.passport and req.user are always empty and that, in fact, serializeUser and deserializeUser are never being called.
Here is the setup of express and passport:
var bodyParser = require('body-parser');
var session = require('express-session');
var MemoryStore = session.MemoryStore;
var _ = require('underscore');
var passport = require('passport');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
app.use(session({
key: 'KEY',
secret: 'SECRET331156%^!fafsdaasd',
store: new MemoryStore({reapInterval: 60000 * 10}),
saveUninitialized: true,
resave: false
}));
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport); // pass passport for configuration
and here is the passport file with authentication strategies:
module.exports = function (passport) {
passport.serializeUser(function (user, done) {
logger.info('SERIALIZE USER');
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
logger.info('DESEIRALIZE USER!');
mysqllib.getConnection(function (err, connection) {
if (err) {
done(err);
}
var sql = "SELECT * FROM users WHERE idusers = ?";
logger.info('sql: ' + sql);
connection.query(sql, [id], function (err, rows) {
connection.release();
var user = {};
user.id = rows[0].idusers;
done(err, user.id);
});
});
});
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
logger.info('CALLING local-signup');
var firstname = req.body.firstname;
var lastname = req.body.lastname;
var role = req.body.role;
mysqllib.getConnection(function (err, connection) {
var sql = "INSERT INTO users VALUES(0, ?, ?, ?, ?, null, ?, 0, null, null, null, null, null, null, 0, 0)";
logger.info('sql: ' + sql);
connection.query(sql, [email, password, firstname, lastname, role], function (err, rows) {
connection.release();
if (err) {
if (err.code == 'ER_DUP_ENTRY') {
logger.info('er_dup_entry');
return done(err);
} else {
logger.info('general err');
return done(err);
}
} else {
logger.info('everything is OK!');
var user = {};
user.id = rows.insertId;
req.session.user_auth = user.id;
return done(null, user);
}
});
});
}));
passport.use(
'local-login',
new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
mysqllib.getConnection(function (err, connection) {
if (err) {
logger.info('getConnection: ' + err);
return done(err);
}
var sql = "SELECT idusers, first_name, last_name, email, phone, dob, address, role, photo1, photo2, photo3, photo4, phonevalidated, uservalidated FROM users WHERE email = " + connection.escape(email) + " AND password = " + connection.escape(password);
connection.query(sql, function (err, rows) {
connection.release();
if (err) {
logger.error("select user", err);
return done(err);
} else if (rows.length) {
var user = rows[0];
user.id = rows[0].idusers;
return done(null, user);
} else {
logger.warn('Incorrect Login credentials, username: ' + email + ' password: ' + password);
return done(null, false, {message: 'unauthorized'});
}
});
});
})
);
};
and, for last, here is how I'm using the strategies in express routes:
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) {
mysend(res, 500, 'Ups. Something broke!');
} else if (info) {
mysend(res, 401, 'unauthorized');
} else {
mysend(res, 200, JSON.stringify(user));
logger.info(req.user);
logger.info(req.session);
}
})(req, res, next);
});
Everything works fine, even in a strategy I can set the value of user's id in the session like this:
req.session.user_id = user.id
and continue to use it manually but I really don't get why serializeUser and deserializeUser aren't being called.
You need to call req.login() in custom callback which will then call serializeUser and set the user object to the session:
app.post('/login', function (req, res, next) {
passport.authenticate('local-login', function (err, user, info) {
if (err) {
mysend(res, 500, 'Ups. Something broke!');
} else if (info) {
mysend(res, 401, 'unauthorized');
} else {
req.login(user, function(err) {
if (err) {
mysend(res, 500, 'Ups.');
} else {
mysend(res, 200, JSON.stringify(user));
}
}
}
})(req, res, next);
});

Resources