I\'m trying to establish a login mechanism using node.js, express and passport.js. The Login itself works quite nice, also sessions are stored nicely with redis but I do hav
My way of doing things:
const isAuthenticated = (req, res, next) => {
if (req.isAuthenticated()) {
return next()
}
res.redirect( `/login?origin=${req.originalUrl}` )
};
GET /login controller:
if( req.query.origin )
req.session.returnTo = req.query.origin
else
req.session.returnTo = req.header('Referer')
res.render('account/login')
POST /login controller:
let returnTo = '/'
if (req.session.returnTo) {
returnTo = req.session.returnTo
delete req.session.returnTo
}
res.redirect(returnTo);
POST /logout controller (not sure if there is 100% ok, comments are welcome):
req.logout();
res.redirect(req.header('Referer') || '/');
if (req.session.returnTo) {
delete req.session.returnTo
}
Clear returnTo middleware (clears returnTo from session on any route except auth routes - for me they are /login and /auth/:provider ):
String.prototype.startsWith = function(needle)
{
return(this.indexOf(needle) == 0)
}
app.use(function(req, res, next) {
if ( !(req.path == '/login' || req.path.startsWith('/auth/')) && req.session.returnTo) {
delete req.session.returnTo
}
next()
})
This approach have two features:
Take a look at connect-ensure-login, which works along side Passport to do exactly what you want!
If you are using connect-ensure-login there is a super-easy, integrated way to do this with Passport using the successReturnToOrRedirect
parameter. When used, passport will send you back to the originally requested URL or fallback to the URL you provide.
router.post('/login', passport.authenticate('local', {
successReturnToOrRedirect: '/user/me',
failureRedirect: '/user/login',
failureFlash: true
}));
https://github.com/jaredhanson/connect-ensure-login#log-in-and-return-to
@chovy and @linuxdan answers have bug with not clearing session.returnTo
if user goes to another page after login redirect (thats doesn't require authentication) and logins through there. So add this code to their implementations:
// clear session.returnTo if user goes to another page after redirect to login
app.use(function(req, res, next) {
if (req.path != '/login' && req.session.returnTo) {
delete req.session.returnTo
}
next()
})
If you do some ajax requests from login page, you can also exclude them.
Another approach is to use flash in ensureAuthenticated
req.flash('redirectTo', req.path)
res.redirect('/login')
And then in GET login
res.render('login', { redirectTo: req.flash('redirectTo') })
In view add hidden field to login form (example in jade)
if (redirectTo != '')
input(type="hidden" name="redirectTo" value="#{redirectTo}")
In POST login
res.redirect(req.body.redirectTo || '/')
Notice that redirectTo will clear after first GET login with it.
In your ensureAuthenticated
method save the return url in the session like this:
...
req.session.returnTo = req.originalUrl;
res.redirect('/login');
...
Then you can update your passport.authenticate route to something like:
app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
res.redirect(req.session.returnTo || '/');
delete req.session.returnTo;
});
I don't know about passport, but here's how I do it:
I have a middleware I use with app.get('/account', auth.restrict, routes.account)
that sets redirectTo
in the session...then I redirect to /login
auth.restrict = function(req, res, next){
if (!req.session.userid) {
req.session.redirectTo = '/account';
res.redirect('/login');
} else {
next();
}
};
Then in routes.login.post
I do the following:
var redirectTo = req.session.redirectTo || '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);