Passport login and persisting session
Solution 1
Is the user now in a session on my server?
No, You need to use the express-session
middleware before app.use(passport.session());
to actually store the session in memory/database. This middleware is responsible for setting cookies to browsers and converts the cookies sent by browsers into req.session
object. PassportJS only uses that object to further deserialize the user.
Should I send the req.session.passport.user back to the client?
If your client expects a user
resource upon login, then you should. Otherwise, I don't see any reason to send the user object to the client.
Do I need the session ID on all future requests?
Yes, for all future requests, the session id is required. But if your client is a browser, you don't need to send anything. Browser will store the session id as cookie and will send it for all subsequent requests until the cookie expires. express-session
will read that cookie and attach the corresponding session object as req.session
.
Testing the Session
passport.authenticate('local')
is for authenticating user credentials from POST body. You should use this only for login route.
But to check if the user is authenticated in all other routes, you can check if req.user
is defined.
function isAuthenticated = function(req,res,next){
if(req.user)
return next();
else
return res.status(401).json({
error: 'User not authenticated'
})
}
router.get('/checkauth', isAuthenticated, function(req, res){
res.status(200).json({
status: 'Login successful!'
});
});
Solution 2
As @hassansin says you need to use a middleware that implement session management. The passport.session() middleware is to connect the passport framework to the session management and do not implement session by itself. You can use the express-session
middleware to implement session management. You need to modify your auth.js in the following way
var passport = require('passport');
var session = require('express-session');
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(app, user){
app.use(session({secret: 'some secret value, changeme'}));
app.use(passport.initialize());
app.use(passport.session());
// passport config
passport.use(new LocalStrategy(user.authenticate()));
passport.serializeUser(function(user, done) {
console.log('serializing user: ');
console.log(user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
user.findById(id, function(err, user) {
console.log('no im not serial');
done(err, user);
});
});
};
Notice that in this case the session engine is using the in memory store and it didn't work if you scale your application and apply load balancing. When you reach this development state something like the connect-redis
session store will be needed.
Also notice that you need to change the secret value used on the session midleware call and use the same value on all application instances.
Solution 3
As per the passport documentation, req.user
will be set to the authenticated user. In order for this to work though, you will need the express-session
module. You shouldn't need anything else beyond what you already have for passport to work.
As far as testing the session, you can have a middleware function that checks if req.user is set, if it is, we know the user is authenticated, and if it isn't, you can redirect the user.
You could for example have a middleware function that you can use on any routes you want authenticated.
authenticated.js
module.exports = function (req, res, next) {
// if user is authenticated in the session, carry on
if (req.user) {
next();
}
// if they aren't redirect them to the login page
else {
res.redirect('/login');
}
};
controller
var authenticated = require('./authenticated');
router.get('/protectedpage', authenticated, function(req, res, next) {
//Do something here
});
Joe Lloyd
I'm Always looking for interesting frontend work in Amsterdam so send me an email at [email protected] if you want to collab. Specialities include React, Node and firebase. I love making a good inhouse dashboard application. I dislike working with Wordpress, PHP and Drupal.
Updated on July 09, 2022Comments
-
Joe Lloyd almost 2 years
Background
I have a MEAN application with CRUD capabilities fully tested with postman. I have been trying to persist login for quite some time now with no luck. I have read and tried the following
- passport docs
- toon io's blog about login
- Express session
- Scotch io, node auth made easy
- Plus a buch of other light reading materials (Lots of SO questions)
But I have only been able to register and log a user in, not persist login with a session.
My App
Here is a link to the full github repo (if you are looking for the latest changes check develop branch)
My Understanding of Auth/Login
Here is my understanding of user login with code examples from my project and screenshot of postman results as well as console logs.
Passport setup
I have the following auth.js file, it configs passport
var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; module.exports = function(app, user){ app.use(passport.initialize()); app.use(passport.session()); // passport config passport.use(new LocalStrategy(user.authenticate())); passport.serializeUser(function(user, done) { console.log('serializing user: '); console.log(user); done(null, user._id); }); passport.deserializeUser(function(id, done) { user.findById(id, function(err, user) { console.log('no im not serial'); done(err, user); }); }); };
This gets called in the server file like
//code before var user = require('./models/user.js'); var auth = require('./modules/auth.js')(app, user); // code after
Routing for login
In my routes I have the login route as follows
router.post('/login', function(req, res, next) { passport.authenticate('local', function(err, user, info) { if (err) { return next(err); } if (!user) { return res.status(401).json({ err: info }); } req.logIn(user, function(err) { if (err) { return res.status(500).json({ err: 'Could not log in user' }); } res.status(200).json({ status: 'Login successful!' }); }); })(req, res, next); });
This route works as tested with postman. I enter the details 'joe' and 'pass' and get the following response.
When this route is hit we can also see in the console that the user is serialized.
So what next?
This is where I get lost. I have a few questions.
- Is the user now in a session on my server?
- Should I send the
req.session.passport.user
back to the client? - Do I need the session ID on all future requests?
Testing the Session
I have a second route setup for testing the session it is as follows
router.get('/checkauth', passport.authenticate('local'), function(req, res){ res.status(200).json({ status: 'Login successful!' }); });
The part
passport.authenticate('local')
(I thought) is there to test if the user session exists before giving access to the route but I never get a 200 response when I run this, even after a login.Does this route expect a
req.session.passport.user
passed in the head or as a data argument on a http request that requires auth?If I missed anything or am understanding something wrong please tell me, any input is appreciated. Thanks all.
-
Joe Lloyd about 8 yearsThe part that is not working is the section "Testing the session". I log in but cant make the request to checkauth. But how does the route know that im the correct user if I don't send it session id? Is there a way to see what sessions are active on the server?
-
Joe Lloyd about 8 yearsThank you Hassansin, session is now working on my app.
-
Bharat Chhabra over 6 yearsWhat will happen if I use req.isAutheticated() instead of req.user on every route to check if a user is logged In or not?
-
StErMi over 6 yearsSo how to keep the user logged in after the node app is restarted? Becuase right now it's only an in-memory session
-
Matheus Bernardi about 5 years@hassansin With Postman my login persist, but, on brower it doesnt. I'm using Angular. What I need to do for persist the login after /login route on Angular?