Redirecting to previous page after authentication in node.js using passport.js
Solution 1
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);
Solution 2
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;
});
Solution 3
Take a look at connect-ensure-login, which works along side Passport to do exactly what you want!
Solution 4
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:
- you can protect some routes with isAuthenticated middleware;
- on any page you can simply click on login URL, and after login return to that page;
Solution 5
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
Related videos on Youtube
Alx
Updated on October 27, 2020Comments
-
Alx over 3 years
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 have some troubles with redirecting the user to where he started from before being prompted to authenticate.
e.g. User follows link
http://localhost:3000/hidden
is then redirected tohttp://localhost:3000/login
but then I want him to be redirected again back tohttp://localhost:3000/hidden
.The purpose of this is, if the user access randomly a page he needs to be logged in first, he shall be redirected to the /login site providing his credentials and then being redirected back to the site he previously tried to access.
Here is my login post
app.post('/login', function (req, res, next) { passport.authenticate('local', function (err, user, info) { if (err) { return next(err) } else if (!user) { console.log('message: ' + info.message); return res.redirect('/login') } else { req.logIn(user, function (err) { if (err) { return next(err); } return next(); // <-? Is this line right? }); } })(req, res, next); });
and here my ensureAuthenticated Method
function ensureAuthenticated (req, res, next) { if (req.isAuthenticated()) { return next(); } res.redirect('/login'); }
which hooks into the
/hidden
pageapp.get('/hidden', ensureAuthenticated, function(req, res){ res.render('hidden', { title: 'hidden page' }); });
The html output for the login site is quite simple
<form method="post" action="/login"> <div id="username"> <label>Username:</label> <input type="text" value="bob" name="username"> </div> <div id="password"> <label>Password:</label> <input type="password" value="secret" name="password"> </div> <div id="info"></div> <div id="submit"> <input type="submit" value="submit"> </div> </form>
-
Dhrumil Shah over 10 yearsHi I am also doing the same the only difference is when user is successfully log in then i will redirect to welcome.html page with message like'Welcome UserName'. hear UserName will be his/her LoginName. Can you show me how to pass text box value to next page ??
-
Alx over 10 yearsnot knowing much I'd guess simply pass it through. According from my example that might be: res.render('hidden', {title: 'hidden page', username: 'Tyrion Lannister'});
-
-
Alx over 11 yearsthanks for your reply. Aren't you setting the redirect /account direct in your code? What happens if you have another link let's say /pro_accounts using the same auth.restrict they would be redirected to /account ....
-
chovy over 11 yearsThat's true. I always redirect to /account after they login, but you could replace it with
req.session.redirect_to = req.path
-
Alx over 11 yearshm.. its not quite I'm looking for. I call ensureAuthenticated to figure out if a user is already authed or not, if not it is redirected to /login. From this view I need a way to get back to /account. Calling req.path within /login gives me simple /login back. Using referer doesn't work either plus its not quite certain if the browser sets the referer properly...
-
chovy over 11 yearsYou need to set
req.session.redirect_to = req.path
inside your auth middleware. Then read it and delete it inlogin.post
route. -
linuxdan over 10 yearsthis isn't a great solution for passport which accepts successRedirect as an option at a point that request isn't available
-
Rob Andren over 9 yearsWorked on the first pass, but I needed to add the following after the redirect in the passport.authenticate route to avoid being taken back to the returnTo address after subsequent logout/login:
req.session.returnTo = null;
See this question for additional info about passport's default logout, which, on examination of source, seems to only clear the session.user, and not the entire session. -
OMGPOP almost 9 yearscan i simply pass around a query like ?callback=/profile instead of session
-
linuxdan almost 9 years@OMGPOP you probably could, but that doesn't sound very secure, is there a reason you don't want to take advantage of the session?
-
OMGPOP almost 9 yearsor you can use req.flash('redirect')
-
OMGPOP almost 9 years@linuxdan not at all. but it's just troublesome to extract the redirect link out and put into session, then when user redirect back, assign back the redirect link from the session
-
phillipwei over 8 years@linuxdan why is it insecure?
-
linuxdan over 8 years@phillipwei - if you are passing around authentication related data on the query string, you are giving hackers implementation details of your authentication system.
-
phillipwei over 8 years@linuxdan but the value here is just the url that the user navigated to originally; I don't think that needs securing does it -- or am I missing something obvious?
-
linuxdan over 8 years@phillipwei I'm not a security expert, so I can't say for sure, but when it comes to authentication I try to err on the side of caution. Session variables are generally more secure than url parameters since they aren't passed around in the open where a simple packet sniffer can pick them up. Is the redirect path an asset worth securing? Maybe not, but I could imagine it being a problem when combined with other possible security vulnerabilities.
-
Alexander Danilov about 8 yearsBUG: user wants to go to /account but he is not authorized, he is redirected to login and session.redirectTo='/account'. Then he goes to '/' and logins through main page, but session.redirectTo still equals '/account' and he redirects to account but should not
-
mikemaccana over 7 yearsThis seems like a duplicate of @jaredhanson's answer
-
matthaeus over 6 yearsInstead of
req.path
I would usereq.originalUrl
since it is a combination of baseUrl and path, see the documentation -
Tom Söderlund over 6 yearsCan you elaborate? I can see
successRedirect
being used on the return route, but then the intended destination (i.e.returnTo
above) would be lost, right? -
user752746 over 5 yearsThere were so many good response here, I was able to use your example and made it work with my project. Thank you!
-
meain almost 5 yearsneed to move the delete line to above the other line, but works otherwise
-
Rich Remer about 4 yearsURLs are not passed in the clear if the site is secured with TLS. And if your site isn't secured with TLS, most OAuth implementations aren't secure either, so it's a non-issue. On the other hand, I have not been able to find a way to pass along a callback URL to Google that Google will return when it comes back.
-
Anton Drukh almost 3 yearsWorth adding that
express-session
needs to be configured so that is saves 'uninitialized' cookies:{ saveUninitialized: true }
. This means that anonymous users will have sessions created for them, and put more pressure against your session store. On the upside, these sessions will store thereturnTo
location and be used after an anonymous user authenticates successfully.