Passport + Node.js / Automatic login after adding user - javascript

I am using passport for authentication and session handling. Everything works fine so far. I implemented a "Sign in" form to add new users to the app. After a user is added I would like to log him/her in automatically.
What is the best way to achieve this - should I redirect to "/login" with the user credentials or is there another/better way(call serializeUser) to do that?
So far I think I did not really understand the way the "done" function (in serializeUser and LocalStrategy) is working or what it is doing ...
Here is my code:
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
authProvider.findUserById('users', id, function (err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy( function(email, password, done) {
authProvider.getUserByEmail('users', email, function(error, user){
if(error) { return done(error); }
if (!user) { return done(null, false, { message: 'Unknown user ' + email });}
if (user.password != password) { return done(null, false);}
return done(null, user);
});
}
));
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login'}),
function(req, res) { res.redirect('/');});
app.post('/sign', function(req, res){
authProvider.saveUser(...do stuff), function(error, user){
if(error){
res.redirect('/sign');
} else {
res.redirect('/');
}
});
});
Does someone know how to do this?

Based on the Passport Guide req.login() is intended for this exact purpose.
This function is primarily used when users sign up, during which req.login() can be invoked to automatically log in the newly registered user.
Modifying krasu's code:
app.post('/sign', function(req, res){
authProvider.saveUser(...do stuff), function(error, user){
if ( error ){
res.redirect('/sign');
} else {
req.login(user, function (err) {
if ( ! err ){
res.redirect('/account');
} else {
//handle error
}
})
}
});
});
The potential error from the login() callback would come from your serializeUser() function.

Please use code from the #Weston answer bellow, because it's more universal and straightforward
Should look something like this
app.post('/sign', function(req, res){
authProvider.saveUser(...do stuff), function(error, user){
if(error){
res.redirect('/sign');
} else {
passport.authenticate('local')(req, res, function () {
res.redirect('/account');
})
}
});
});
I don't sure about name of strategy, but by default LocalStrategy should provide 'local' name
http://passportjs.org/guide/authenticate/

Try with:
app.post('/sign', function(req, res){
authProvider.saveUser(...do stuff), function(error, user){
passport.authenticate('local', (err, user) => {
req.logIn(user, (errLogIn) => {
if (errLogIn) {
return next(errLogIn);
}
return res.redirect('/account');
});
})(req, res, next);
});
});

Related

session user sometimes has value sometimes undefined

I have a little problem, and I have already spent one day trying to solve it with no results. I used passport.js, and the login and session work well, if I access address without login then I will be kicked to login page. But I have a little problem, when I fill datasource of datatable (cart.html) with routing from index.js, req.session.user will be undefined. But I checked in cart.html in routing of index.js, it has a value. I would appreciate anyone who can help me, thanks in advance.
Here is my routing cart.html of index.js
app.get('/cart',(req,res)=>{
//res.render('cart');
if (req.session.user) {
var userdata=req.session.passport.user;
res.render('cart',{username: userdata[0].USER,level:userdata[0].IDLEVEL});
}else {
res.redirect('/login');
}
});
here my passport :
passport.use(new LocalStrategy({passReqToCallback: true},
function(req,username, password, done) {
db.getConnection(function (err, connection) {
SQL="SELECT * FROM muser WHERE USER = ? and PASSWORD = ?";
connection.query(SQL, [username, password], function (err, rows) {
connection.release();
if(err) return done(err);
// if user not found
if (rows.length <= 0) {
return done('Cannot login');
//console.log(username+' '+password);
}
req.session.user=rows;
return done(null, rows);
});
});
}
));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
db.getConnection(function (err, connection) {
connection.query('SELECT * FROM muser WHERE ID = ?', [id], function(err, user) {
connection.release();
if(err) return done(err);
done(null, user);
});
});
});
function isAuthenticated(req, res, next) {
if (req.isAuthenticated()) return next();
res.redirect('/login');
}
app.get('/', isAuthenticated, function(req, res) {
res.render('home');
});
app.use(passport.initialize());
app.use(passport.session());
app.post('/login',
passport.authenticate('local', {
failureRedirect: '/login' }),
function(req, res) {
if (req.isAuthenticated()) {
res.redirect('/home');
}else {
res.redirect('/');
}
}
);
here my cart.html's codes
$("#dgcart").DataGrid({
dataSource: "http://192.168.10.120:5556/getcart" (routing below to get data of cart)
...});
here my bug cannot access req.session.user will be undefined
app.get('/getcart',(req,res)=>{
if (req.session.user) {
var userdata=req.session.passport.user;
SQL="SELECT TC.ID,TC.IDITEM,TC.KODEBARANG,TC.NAMA,SUM(TC.QTY) AS QTY,TC.PRICE, TC.SUBTOTAL"+'\n'+
"FROM cart TC "+'\n'+
"WHERE TC.IDUSER="+userdata[0].ID+'\n'+
"GROUP BY TC.KODEBARANG,TC.IDITEM,TC.KODEBARANG,TC.NAMA,TC.PRICE,TC.SUBTOTAL,TC.IDUSER";
rowsalasql=alasql(SQL);
console.log(SQL+' '+rowsalasql);
res.send(JSON.stringify(rowsalasql));
}else {
... will be here because undefined
}
});

PassportJS Authentication Won't Execute on Login Attempt (browser times out)

I'm very new to Passport & Node, I've been trying to solve an issue for several days without being able to find a solution that already exists on SO or the internet. I'm getting no errors or anything on login attempt, nothing in chrome dev, nothing in gitbash. Only problem is page never redirects, never seems to get through the passport.authenticate function inside my auth controller (/dologin). When I attempt a login, the browser never stops loading (ex. the circle keeps spinning for chrome), I have no problems with internet or anything of that nature, I only have this problem when implementing the login feature. One thing that I suspect might be an issue is that I do not have my localstrategy/serialization/deserialization in the right spot but I have tried it in app.js as well so at this point I'm really just too confused.
app.js - I tried including the initialize > session > localstrategy > serialize > deserialize in here, but it also didn't work so I just left initialize and session
// // Init passport authentication
app.use(passport.initialize());
// persistent login sessions
app.use(passport.session());
index.js
// route for login action
router.post('/dologin', auth.doLogin);
authController.js
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 (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Invalid password' });
}
});
});
}));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.getUserById(id, function (err, user) {
done(err, user);
});
});
userController.doLogin = function(req, res) {
passport.authenticate('local', { successRedirect: '/', failureRedirect: '/doLogin', failureFlash: true }),
function (req, res) {
res.redirect('/');
}
};
users.js (model) - includes getUserByUsername, comparePassword, getUserbyId
module.exports.getUserByUsername = function(username, callback){
var query = {username: username};
User.findOne(query, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback){
bcrypt.compare(candidatePassword, hash, function(err, isMatch){
if (err) throw err;
callback(null, isMatch);
});
}
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}

passport authentication: user undefined after login

I am trying to authenticate user ,when i try to login user after successful verification using req.logIn but it doesn't worked
router.post('/login', function(req, res, next) {
passport.authenticate('login',function (cb,data) {
//user verfication success
if(data){
req.logIn({user:"shamon"},function(err,result){
console.log("result",result,err)
res.send('login success');
});
}
})(req,res,next);
});
this console.log("result",result,err) gives me undefined,undefined
when i log req.user after logged i got undefined error
UPDATE
var LocalStrategy = require('passport-local').Strategy
module.exports = function(passport){
passport.use('local',new LocalStrategy({
usernameField: 'email',
passwordField: 'password'
},function (username,password,done) {
console.log('inside passport');
return done(null,true);
}));
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null,user);
});
}
Just grab my implementation of password local-strategy. This is working for sure, but you will need to slightly modify it:
The strategy:
// Serialize
passport.serializeUser(function (user, done) {
done(null, user.id);
});
// Deserialize
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (!user)
return done(null, false, req.flash('loginError', 'No such user found.')); // req.flash is the way to set flashdata using connect-flash
// if the user is found but the password is wrong
if (!user.validPassword(password))
return done(null, false, req.flash('loginError', 'Oops! Wrong password.')); // create the loginMessage and save it to session as flashd
// all is well, return successful user
return done(null, user);
});
}
));
And the route:
router.post('/login', function(req, res, next) {
passport.authenticate('local-login', function(err, user, info) {
if (err) {
return next(err);
}
if (!user) {
return res.send({alert: req.flash('loginError')});
}
req.logIn(user, function(err) {
if (err) {
return next(err);
}
return res.send({redirect: '/'});
});
})(req, res, next);
});

How to clear cookies when error deserialize a user using passport.js?

When I use passport.deserializeUser handler:
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
If done was called with an err, do I got a chance to clear the cookies and redirect users to /login page? Does that functionality depends on the strategy I'm using? E.g. using failureRedirect option of local strategy?
I've used continuation local storage and register it before passport.
var createNamespace = require('continuation-local-storage').createNamespace;
var writer = createNamespace('express');
app.use((req,res,next) => {
namespace.bindEmitter(req);
namespace.bindEmitter(res);
namespace.run(() => {
writer.set('req', req)
writer.set('res', res);
next()
});
});
app.use(passport.initialize(), passport.session());
Then in your deserialize you can access the same namespace and pull the value out:
var getNamespace = require('continuation-local-storage').getNamespace;
var reader = getNamespace('express');
function deserialize(key, done) {
try {
...
} catch(e) {
reader.get("req").logout()
}
}
What you can do is use the req.session.destroy method.... So in your case:
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
req.session.destroy();
});
});
And that techinacly may not "Fully" Log out the person in your app, so if you want an alternative you can use the clearCookie method provided:
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
req.session.destroy(function() {
res.clearCookie('connect.sid');
res.redirect('/');
});
});
});
Like #amanuel2 noted, you can use req to destroy the session. Passport will pass req to deserializeUser if you ask for it:
passport.deserializeUser(function(req, id, done) {
User.findById(id, function(err, user) {
if (err) {
req.session.destroy(function() {
return done(err)
}
}
done(null, user);
});
});
So you can destroy the session, redirect, etc if there is a problem deserializing the session.

How to redirect after error inside of PassportJS/Express?

When configuring passport using Express and NodeJS, I am throwing an error if the user has an invalid email address. After this error I would like to redirect to a failure page giving them instructions on how to log in correctly. Is there are a better way to do this? If not, how would I go about catching the error in some fashion and redirecting to a new page.
passport.use(new GoogleStrategy({
clientID : auth.googleAuth.clientID,
/* Settings redacted for brevity */
},
function(token, refreshToken, profile, done) {
User.findOne(
{
"google.id" : profile.id
},
function(err, user) {
if (err) return done(err)
if (user) return done(null, user)
else {
if (email.indexOf("lsmsa.edu") > -1) {
// Code redacted for brevity
} else {
done(new Error("Invalid email address"))
}
}
}
)
}))
I think you can use this:
Redirects
A redirect is commonly issued after authenticating a request.
app.post('/login',
passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
In this case, the redirect options override the default behavior. Upon
successful authentication, the user will be redirected to the home
page. If authentication fails, the user will be redirected back to the
login page for another attempt.
Or this:
Custom Callback
If the built-in options are not sufficient for handling an
authentication request, a custom callback can be provided to allow the
application to handle success or failure.
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);
});
Please read the document:
https://www.passportjs.org/concepts/authentication/downloads/html/#middleware
Note: I quite like BlackMamba's answer as well, adding the custom callback / redirect is a perfectly acceptable option.
Simply add your own error handling middleware to Express:
passport.use(new GoogleStrategy({
clientID : auth.googleAuth.clientID,
/* Settings redacted for brevity */
},
function(token, refreshToken, profile, done) {
User.findOne({
"google.id" : profile.id
},
function(err, user) {
if (err) return done(err)
if (user) return done(null, user)
else {
if (email.indexOf("lsmsa.edu") > -1) {
} else {
// Throw a new error with identifier:
done(throw {
type: "invalid_email_address",
code: 401,
profileId: profile.id
}));
}
}
}
)
}));
// The error handling middleware:
app.use(function(e, req, res, next) {
if (e.type === "invalid_email_address") {
res.status(e.code).json({
error: {
msg: "Unauthorized access",
user: e.profileId
}
});
}
});
You'll notice I modified this answer a little bit with a more robust error composition. I've defined the code property of the error to match the applicable HTTP status code -- in this case, 401:
// callback
done(throw {
// just a custom object with whatever properties you want/need
type: "invalid_email_address",
code: 401,
profileId: profile.id
}));
In the error handling, we simply check the type is invalid_email_address (you can make it whatever you want, but it should be consistent across your app) and then write the error out using the "code" as the HTTP status code:
// e is the error object, and code is the custom property we defined
res.status(e.code).json({
error: {
msg: "Unauthorized access",
user: e.profileId
}
});
Here's a self-contained working example with a redirect:
var express = require('express');
var app = express();
app.all('*', function(req, res) {
throw {type: "unauthorized", code: 401}
})
app.use(function(e, req, res, next) {
console.log(e);
if (e.code === 401) {
res.redirect("/login")
} else {
res.status(500).json({error: e.type});
}
});
app.listen(9000);

Categories

Resources