Express Passport.js not persisting user object in session - node.js

I have an Express application running on port 3000. The front end runs on port 80, so this is a CORS application. Users are stored in an SQL server database. I'm using passport as the authentication method with Local Strategy as well as express session middleware. The application is a Single page application and all requests sent to server are done via ajax. Users log in on page and the credentials are sent and if authentication is successful, userID as well as username and FullNmae are supposed to be persisted to a session.
I have a lot of things wrong with this: The main thing is that after logging in, express saves the username and other data using passport on to a new session andsends back an html snippet to replace the body tag on the page. However, to test that the user object persists, I call the /create-user route and it says that the user object is not there. Additionally, a new session starts with every request (I check the logs and see that a different session ID is displayed each time). Not only that but at one point I was able to see the session cookie in the browser but I can no longer see it anymore. I tried to go back to the point where I could see the cookie but it still didn't appear!
I've been busting my head for hours and can't figure out why deserializeUser is not called nor why the data is not persisted. Where am I going wrong?
Note: some obvious code ommitted (app.listen(), require statements, etc.)
/* ------ CONFIGURATIONS ------ */
const app = express();
const mssqlConfig = JSON.parse(fs.readFileSync("mssql-config.json", "utf8"));
passport.use(new LocalStrategy(
function loginAuthentication(username, password, done) {
let connPool = new mssql.ConnectionPool(mssqlConfig);
connPool.connect(error => {
if (error) {console.log(error); return done(error);}
ps = new mssql.PreparedStatement(connPool);
ps.input('username', mssql.NVarChar(20));
ps.input('password', mssql.NVarChar(50));
ps.prepare('SELECT FullName, fldLoginName, fldEmployeeID, fldPassword FROM tblEmployees WHERE fldLoginName = #username AND fldPassword = #password;', error => {
if (error) {console.log(error); return done(error);}
ps.execute({username, password}, (error, result) => {
if (error) {console.log(error); return done(error);}
console.log(result);
if (result.recordset.length == 0) {
return done(null, false, {message: "There is no user with those credentials!"});
} else if (result.recordset[0].fldLoginName != username || result.recordset[0].fldPassword != password) {
return done(null, false, {message: "Username or password is incorrect!"})
} else {
return done(null, {
ID: result.recordset[0].fldEmployeeID,
username: result.recordset[0].fldLoginName,
fullName: result.recordset[0].FullName
});
}
ps.unprepare(error => console.log(error));
});
});
});
}
));
passport.serializeUser((user, done) => {
done(null, JSON.stringify(user));
})
passport.deserializeUser((user, done) => {
console.log(user);
done(null, JSON.parse(user));
});
/* ----- MIDDLEWARE ------ */
app.use(function allowCrossDomain(request, response, next) { // CORS
// intercept OPTIONS method
response.header('Access-Control-Allow-Credentials', true);
response.header('Access-Control-Allow-Origin', request.headers.origin);
response.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
response.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
response.header('Access-Control-Max-Age', '60');
if ('OPTIONS' == request.method) {
response.sendStatus(200);
} else {
next();
}
});
app.use(bodyParser.json());
app.use(session({
secret:"long string of characters",
name:'officetools-extensions',
saveUninitialized:false,
resave:false,
cookie:{secure:false, httpOnly:true, maxAge:86400000, domain:"http://officetools-extensions"},
store: new MemoryStore({checkPeriod:86400000})
}));
app.use(passport.initialize());
app.use(function checkRestrictedURL(request, response, next){
console.log(request.url);
if (!request.url.match(/^\/login$/g)) {
console.log("passed");
passport.session()(request, response, next);
} else {
next();
}
});
/* ------ ROUTES ------ */
app.post('/login', bodyParser.urlencoded({extended:false}), (request, response, next) => {
passport.authenticate('local', {session:true}, (error, user, info) => {
if (error) { error.status = 500; return next(error); }
if (info) { let err = new Error(info.message); err.status = 400; return next(err);}
if (!user) { return response.status(401).send("User could not be logged in!"); }
console.log(request.sessionID);
console.log(user);
console.log(request.session);
request.logIn(user, function loginCallBack(error) {
if (error) { error.status = 500; return next(error);}
console.log("after login", request.session);
console.log(request.isAuthenticated());
return response.sendFile(path.join(__dirname + "/templates/barcodes.html"));
})
})(request, response, next);
});
app.get("/current-user", (request, response, next) => {
console.log(request.user, request.session);
console.log(request.sessionID);
console.log(request.isAuthenticated());
if (request.user) {
response.header("Content-Type", "application/json");
return response.send(request.user);
}
else { return response.status(401).send("There is no user currently logged in!"); }
});

I figured it out. I just had to remove the domain property on the session settings. That made it work.

Related

Setting up Global sessions by using Passport Consumer strategy

I need some help with setting up Passport Consumer Strategy and integrating it into "locals" (Right now, we have the local strategy working just fine). We have tried several approaches but with no luck on it working 100%. The below code is not the complete code, we have taken out some of it so this post doesn't get too long. Any help with this would greatly be appreciate. There could be compensation as well if someone can get us over this hurdle.
So one question is, if the user is authenticated by the consumer key and secret, how does Passport store the session variables so they are used throughout the site?
Second question, how do we handle the user after it passes the authentication process?
Both local and consumer need to be working.
Consumer key and secret using a POST by a Provider <- I can show some of the post if needed.
This needs to be OAuth1 Only, as of right now, OAuth2 isn't an option.
This is for a single sign-on authentication.
I can supply a consumer session output if needed.
Ultimately, we would like the local strategy and the consumer strategy working with the same "locals" global variables. As far as I can tell, we can authenticate the consumer, retrieve the user from our DB, create a session and can tell if user is "ensureAuthenticated".
Here is what we have working right now.
Local strategy is authenticating correctly.
We render the pages with these local variables:
"Omitted most of the code to save time."
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
...
function renderPage(req, res, pageName, pageTitle){
res.render(pageName, {
pageName: pageTitle,
username: req.user ? req.user.username : '',
...
Consumer strategy is authenticating by a POST request from a "Provider"
We have tried adding the Consumer strategy to the authentication.
server.js
//=================================================================
// The Authentication Module will bind to POST /login route
//=================================================================
authentication.initLocalStrategyRoutes(app);
ADDED -> authentication.initConsumerStrategyRoutes(app);
passport.authenticate('local', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
ADDED -> passport.authenticate('consumer', {successReturnToOrRedirect: '/', failureRedirect: '/login'});
authentication.js (omitted code)
module.exports = function(siteConfig, defaultRedirectPage, server, sessionStore, log) {
var passport = require('passport')
...
, ConsumerStrategy = require('passport-http-oauth').ConsumerStrategy
, TokenStrategy = require('passport-http-oauth').TokenStrategy
, LocalStrategy = require('passport-local').Strategy;
var auth = {};
var authenticationRedirects = { successRedirect: '/', failureRedirect: '/login' };
passport.serializeUser(function(user, done) {done(null, user);});
passport.deserializeUser(function(obj, done) {done(null, obj);});
auth.authenticate = function(email, password, callback) {
email = email.toLowerCase();
userController.findUserByUsernameWithPermissions(email,
function(err, user) {
if (err) return callback(err);
if (!user) return callback(null, null, 'Incorrect username.');
bcrypt.compare(password, user.password_hash, function(err, res) {
if(err){return callback(err);
} else if (!res) {return callback(null, null, 'Incorrect password.');
} else {if (user.account_state>0) {callback(null, user);} else {return callback(null, null, '/reset?rand='+user._id);}}
});
}
);
}
auth.initLocalStrategyRoutes = function(app){
passport.use(new LocalStrategy(auth.authenticate));
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err);
if (!user) return res.send({success: false, message: info});
req.logIn(user, function(err) {
if (err) { return next(err); }
res.send(req.user);
});
}) (req, res, next);
});
}
auth.initConsumerStrategyRoutes = function(app){
// passport.use(new LocalStrategy(auth.authenticate));
console.log('app: ', app)
passport.use('consumer', new ConsumerStrategy(
function(key, done) { console.log('starting ConsumerStrategy');
dbConsumerKey.findByConsumerKey({consumerKey: key}, function(err, consumerKey) {
if (err) { return done(err); }
if (!consumerKey) {
var errCode = dbError.find({name:'no_resource_link_id'}, function(err, errorCodes) {
console.log('statusText: ', errorCodes[0]["statusText"]);
return errorCodes[0]["statusText"];
});
return done(null, errCode);
} else {
if (!consumerKey[0]["consumerKey"]) { return done(err); }
if (!consumerKey[0]["consumerSecret"]) { return done(err); }
// return done(null, consumerKey[0]["consumerKey"], consumerKey[0]["consumerSecret"]);
return done(null, consumerKey[0], consumerKey[0]["consumerSecret"]);
}
});
},
function(requestToken, done) {
dbRequestTokens.find(requestToken, function(err, token) {
console.log('inside requestToken');
if (err) { return done(err); }
var info = { verifier: token.verifier,
clientID: token.clientID,
userID: token.userID,
approved: token.approved
}
done(null, token.secret, info);
});
},
function(timestamp, nonce, done) {
done(null, true)
}
));
};
auth.initTokenStrategyRoutes = function(app){}
auth.addUser = function(username, email, password, callback){auth.authenticate(username, "pass", callback);}
return auth;
};
The authentication.js strategy does validate the consumer key and secret. but it doesn't create the session variable we are wanting. We would like the consumer strategy code to be in the authentication.js file.
Now here is another approach, we created a separate files called consumerkey.js
This direction works to a point. We can output the passport session either on the screen or on the command line.
var passport = require('passport')
exports.launchLti = [
passport.authenticate('consumer', { session: false/true [tried both] }),
function(req, res) {
db.findByStudentUserId({lis_person_contact_email_primary:
req.body.lis_person_contact_email_primary}, function(err, user) {
req.logIn(user, function(err) {
req.user.username = user[0].lis_person_contact_email_primary;
...
// req.session.save(function(){
// res.redirect('/classes');
res.redirect(200,'/');
// });
});
})
// res.render('launch', {launch: launch});
}
}]
I solved this issue by changing some of my code structure.
app.pst('/launch/lti/:id', function(req, res, next) {
passport.authenticate('consumer', {failureRedirect: '/login'}),
dbConsumerKey.findByStudentUserId({},
function(err, user) {
if (err) console.log(err, user);
req.logIn(user, function(err) {
if (err) return err;
ADDED -> req.session.valid = true;
ADDED -> res.redirect('/');
});
}
});
});
and modifying the render page function to adapt to the incoming information.
function renderPage(req, res, pageName, pageTitle){
...
this is where the locals are created
...
this allowed me to use my current local strategy as is and adding a totally different strategy route but making the session correctly.

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.

Clear cookie does't destroy passport session

i use koa-passport and passport-local
init strategy
passport.use(new LocalStrategy(
(username, password, cb) => {
db.findByUsername(username, (err, user) => {
return cb(null, user);
});
}));
login
authorization.post('/login', function(ctx, next) {
return passport.authenticate('local', function(user,error) {
if (!user) {
ctx.status = 401;
ctx.body = { success: false, ...error }
} else {
ctx.body = { success: true }
ctx.status = 200;
return ctx.login(user)
}
})(ctx, next)
})
After delete cookies, browser still have access to private url and if i login again with same user then response does't contains "Set-Cookie" header with session id. How can i fix it?
that happen because i use koa-proxy.
Auth logic is located on another server and i use koa-proxy for proxy requests. Now i delete koa-proxy and use http-proxy and http-proxy-rules and all ok.

Passport.js authentication Failure

I tried to write a username/password authentication server based on this project:
https://github.com/tutsplus/passport-mongo.git
However I always receive a "Can\'t set headers after they are sent." error.
I don't want to use any login session so I removed all the code related to that.
Here is my code:
In app.js
......
// Configuring Passport
var passport = require('passport');
app.use(passport.initialize());
// Initialize Passport
var initPassport = require('./libs/auth/init');
initPassport(passport);
var routes = require('./routes/index')(passport);
app.use('/api', routes);
......
In ./libs/auth/init.js:
var signin = require('./signin');
var createuser = require('./createuser');
var User = require('../../models/user');
module.exports = function(passport) {
// Setting up Passport Strategies for Login and SignUp/Registration
signin(passport);
createuser(passport);
};
The signin.js:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../../models/user');
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport) {
passport.use('signin', new LocalStrategy({
passReqToCallback : true
},
function(req, username, password, done) {
// check in mongo if a user with username exists or not
User.findOne({'username' : username},
function(err, user) {
// In case of any error, return using the done method
if (err) {
return done(err);
}
// Username does not exist, log the error and redirect back
if (!user) {
console.log('User Not Found with username ' + username);
return done(null, false);
}
// User exists but wrong password, log the error
if (!isValidPassword(user, password)){
console.log('Invalid Password');
return done(null, false); // redirect back to login page
}
// User and password both match, return user from done method
// which will be treated like success
return done(null, user);
}
);
})
);
var isValidPassword = function(user, password){
return bCrypt.compareSync(password, user.password);
}
};
,which is almost the same as the original project
Also the createuser.js is almost the same as the original project:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../../models/user');
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport) {
passport.use('createuser', new LocalStrategy({
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, username, password, done) {
var findOrCreateUser = function() {
// find a user in Mongo with provided username
User.findOne({'username' : username}, function(err, user) {
// In case of any error, return using the done method
if (err) {
console.log('Error in SignUp: ' + err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: ' + username);
return done(null, false);
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.username = username;
newUser.password = createHash(password);
// save the user
newUser.save(function(err) {
if (err) {
console.log('Error in Saving user: ' + err);
throw err;
}
console.log('User Registration successful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
};
The model file:
var mongoose = require('mongoose');
module.exports = mongoose.model('User',{
id: String,
username: String,
password: String
});
The ./routes/index.js is very different from the origin file. Because I am trying to implement user authentication apis, I want to send back some json data after user authentication instead of redirecting them to another url.
var express = require('express');
var router = express.Router();
module.exports = function(passport) {
router.post('/signin', function(req, res, next) {
passport.authenticate('signin', {session : false},
function(err, user, info) {
if (err) {
res.json({
message: "Internal Server Error!"
})
} else if (!user) {
res.json({
message: "No Such User!"
})
}
req.logIn(user, function(err) {
if (err) {
res.json({
message: "Login Failure!"
})
}
res.json({
message: "Login Success!"
})
});
})(req, res, next);
});
router.post('/createuser', function(req, res, next) {
passport.authenticate('createuser', {session : false},
function(err, user, info) {
if (err) {
res.json({
message: "Internal Server Error!"
})
} else if (!user) {
res.json({
message: "User Creation failure!"
})
}
res.json({
message: "Create User Success!"
})
})(req, res, next);
});
return router;
};
However this seems doesn't work well. For the signin api I receive that error message every time I make a request from curl, like:
curl --data "username=2232&password=223" http://localhost:3000/api/signin
For the createuser api only when create user succeeds it doesn't crash. Otherwise I will still receive that error message.
BTW, I am not sure what the done method is doing under the hood. Anyone can give me some details?
I would be appreciated if anyone can answer this question as well:
This is the first time I tried to design an web api. What I am trying to do seems odd to me: The server receives a username and password, then it looks it up in the database, if it finds it then just tell the client "hey I found you!". Then no side effect occurs.
I don't think this is the right way how authentication api works. I would expect the server generate some kind of access key together with an expiration time. However I don't find passport.js has the capacity to do that. Am I using the wrong lib to do the authentication api with node.js?
In your routes file you need to use return when sending the response, because just calling the res.json method the execution of function is not stopped and the server tries to send two responses, that's what the error says you.
You should modify your code:
router.post('/signin', function(req, res, next) {
passport.authenticate('signin', {session : false},
function(err, user, info) {
if (err) {
return res.json({
message: "Internal Server Error!"
})
} else if (!user) {
return res.json({
message: "No Such User!"
})
}
req.logIn(user, function(err) {
if (err) {
return res.json({
message: "Login Failure!"
})
}
return res.json({
message: "Login Success!"
})
});
})(req, res, next);
});
router.post('/createuser', function(req, res, next) {
passport.authenticate('createuser', {session : false},
function(err, user, info) {
if (err) {
return res.json({
message: "Internal Server Error!"
})
} else if (!user) {
return res.json({
message: "User Creation failure!"
})
}
return res.json({
message: "Create User Success!"
})
})(req, res, next);
});

Express Passport (node.js) error handling

I've looked at how error handling should work in node via this question Error handling principles for Node.js + Express.js applications?, but I'm not sure what passport's doing when it fails authentication. I have the following LocalStrategy:
passport.use(new LocalStrategy({ usernameField: 'email', passwordField: 'password' },
function(email, password, next) {
User.find({email: UemOrUnm}, function(err, user){
if (err) { console.log('Error > some err'); return next(err); }
if (!user) { console.log('Error > no user'); return next('Incorrect login or password'); }
if (password != user.password) {
return next(Incorrect login or password);
}
return next(null, user);
});
}
));
After I see 'Error > some err' console printout, nothing else happens. I would think it should continue on the the next path with an error parameter, but it doesn't seem to do that. What's going on?
The strategy-implementation works in conjunction with passport.authenticate to both authenticate a request, and handle success/failure.
Say you're using this route (which is passed an e-mail address and a password):
app.post('/login', passport.authenticate('local', {
successRedirect: '/loggedin',
failureRedirect: '/login', // see text
failureFlash: true // optional, see text as well
});
This will call the code in the strategy, where one of three conditions can happen:
An internal error occurred trying to fetch the users' information (say the database connection is gone); this error would be passed on: next(err); this will be handled by Express and generate an HTTP 500 response;
The provided credentials are invalid (there is no user with the supplied e-mail address, or the password is a mismatch); in that case, you don't generate an error, but you pass a false as the user object: next(null, false); this will trigger the failureRedirect (if you don't define one, a HTTP 401 Unauthorized response will be generated);
Everything checks out, you have a valid user object, so you pass it along: next(null, user); this will trigger the successRedirect;
In case of an invalid authentication (but not an internal error), you can pass an extra message along with the callback:
next(null, false, { message : 'invalid e-mail address or password' });
If you have used failureFlash and installed the connect-flash middleware, the supplied message is stored in the session and can be accessed easily to, for example, be used in a template.
EDIT: it's also possible to completely handle the result of the authentication process yourself (instead of Passport sending a redirect or 401):
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (! user) {
return res.send({ success : false, message : 'authentication failed' });
}
// ***********************************************************************
// "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."
// Source: http://passportjs.org/docs
// ***********************************************************************
req.login(user, loginErr => {
if (loginErr) {
return next(loginErr);
}
return res.send({ success : true, message : 'authentication succeeded' });
});
})(req, res, next);
});
What Christian was saying was you need to add the function
req.login(user, function(err){
if(err){
return next(err);
}
return res.send({success:true});
});
So the whole route would be:
app.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (! user) {
return res.send(401,{ success : false, message : 'authentication failed' });
}
req.login(user, function(err){
if(err){
return next(err);
}
return res.send({ success : true, message : 'authentication succeeded' });
});
})(req, res, next);
});
source: http://passportjs.org/guide/login/
You need to add req.logIn(function (err) { }); and do the success redirect inside the callback function
Some time has passed and now the most right code will be:
passport.authenticate('local', (err, user, info) => {
if (err) {
return next(err); // will generate a 500 error
}
// Generate a JSON response reflecting authentication status
if (!user) {
return res.status(401).send({ error: 'Authentication failed' });
}
req.login(user, (err) => {
if (err) {
return next(err);
}
return res.status(202).send({ error: 'Authentication succeeded' });
});
});
I found this thread very useful!
https://github.com/jaredhanson/passport-local/issues/2
You could use this to return error and render it in form.
app.post('/login',
passport.authenticate('local', { successRedirect: '/home', failWithError: true }),
function(err, req, res, next) {
// handle error
return res.render('login-form');
}
);
This is what I got after console.log(req) at the failler route.
const localStrategy = new LocalStrategy({ usernameField: "email" }, verifyUser);
passport.use(localStrategy);
const authenticateWithCredentials = passport.authenticate("local", {
failureRedirect: "/api/auth/login-fail",
failureMessage: true,
});
validation method find your user from db and throw error to the cb if there is any
const verifyUser = async (email, password, cb) => {
const user = await User.findOne({ email });
if (!user) return cb(null, false, { message: "email/password incorrect!" });
const isMatched = await user.comparePassword(password);
if (!isMatched)
return cb(null, false, { message: "email/password incorrect!" });
cb(null, {
id: user._id,
email,
name: user.name,
});
};
now setup your route
router.post("/sign-in", authenticateWithCredentials,(req, res) => {
res.json({user: req.user})
});
router.get("/login-fail", (req, res) => {
let message = "Invalid login request!";
// if you are using typescript cast the sessionStore to any
const sessions = req.sessionStore.sessions || {};
for (let key in sessions) {
const messages = JSON.parse(sessions[key])?.messages;
if (messages.length) {
message = messages[0];
break;
}
}
res.status(401).json({ error: message });
});

Resources