How to handle expired routes / sessions in express? - node.js

I have my Express app setup using Passport for authorization. When a new user signs up, I want to do an email verification. So, after the user posts credentials, the /signup route gets the request and with success redirects to /sendmail for verification.
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/sendmail',
failureRedirect : '/signup'
}));
Also, to prevent unauthorized session, within the /signup route, the user is logged out and the session is destroyed.
app.get('/sendmail', function(req, res) {
res.render('mailsent.ejs', {
message: 'An email with verification link has been sent to ' + req.user.email + '. Please follow the link in your mail to verify your account before logging in.'
});
/* From keeping user authenticated after signup (not verfied yet)*/
req.logOut();
req.session.destroy();
}
});
My question is, as the session is already destroyed, browser gets nothing back when the end-user refreshes the browser, or directly accesses the /sendmail route. How to prevent this. In other words, in the app.get('/sendmail') route, how would I check if the session is on(valid req object) else redirect to '/'.

You can either use a middleware route like so:
app.use('/sendmail', function(req, res, next) { // Middleware for only the `/sendmail` route
if (req.session.authenticated) {
next();
} else {
res.redirect("/");
}
});
Or just put it right into your route:
app.get('/sendmail', function(req, res) {
if (!req.session.authenticated) {
return res.redirect("/"); // Redirect to home page if not authenticated
}
res.render('mailsent.ejs', {
message: 'An email with verification link has been sent to ' + req.user.email + '. Please follow the link in your mail to verify your account before logging in.'
});
/* From keeping user authenticated after signup (not verfied yet)*/
req.logOut();
req.session.destroy();
}
});

Related

Prevent User from logging again in passportjs if they are authenticated once?

I am using PassportJS, and signup and login functions are working quite smooth.
The only problem I am facing with PassportJS (I am using sessions too), that even when the user had logged in, they can again go back to the signup/login url and do signup and/or login back!
This is tempting me. If anyone has a fix/suggestion, please put it down.
UPDATE - 1
Glimpse of myroutes.js: (Using PassportJS along with connet-ensure-login.
app.get('*', function(req, res, next) {
if (req.url.indexOf('/users/login') < 0 &&
req.url.indexOf('/users/signup') < 0) {
req.session.returnTo = null;
}
next();
});
// =====================================
// HOME PAGE (with login links) ========
// =====================================
app.get('/', sabSettings, function(req, res) {
Setting.findOne(function(err, setting) {
if (err)
throw err;
// console.log(setting);
res.render('index', { title: 'eduBird | Reach the glory', setting: req.setting }); // load the index file
});
});
// =====================================
// LOGIN ===============================
// =====================================
// show the login form
app.get('/login', sabSettings, function(req, res) {
// render the page and pass in any flash data if it exists
res.render('login', {
message: req.flash('loginMessage'),
errors: req.flash('error'),
title: 'Login | eduBird',
setting: req.setting
});
});
// process the login form
app.post('/login', passport.authenticate('local-login', {
successReturnToOrRedirect: '/loggedin',
failureRedirect: '/login',
failureFlash: true
}));
// =====================================
// SIGNUP ==============================
// =====================================
// show the signup form
app.get('/signup', sabSettings, function(req, res) {
// render the page and pass in any flash data if it exists
process.nextTick(function() {
res.render('signup', {
message: req.flash('signupMessage'),
errors: req.flash('error'),
title: 'Register | eduBird',
setting: req.setting
});
});
});
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successReturnToOrRedirect: '/profile/welcome',
failureRedirect: '/signup',
failureFlash: true
}));
You have not created any sort of access control, but don't worry we will first go through how Passport works and use this to address the problem.
When the user submits a login form, a POST request to our specified path is made resulting in the execution of the passport.authenticate.
The authenticate middleware for that route is configured to handle the local strategy, passport will invoke your implementation of the local strategy.
If an error occurs while interacting with our database, we invoke done(err). Otherwise if the user is not found or the passwords do not match, we invoke done(null, false). If successful we invoke done(null, user).
Calling done will return us to the passport.authenticate and the corresponding redirect will be executed.
At this point, if the sign-in was successful, the user object (from done(null, user)) is attached to the request and you can access the user object through req.user.
The main idea is if the user object is not attached to the request it means the user is not logged in, so we can control our application behaviour for logged in
users with req.user. For example:
// If the user object does not exist it means the user is not logged in
if (!req.user) {
res.render('signin');
} else {
// If the user object exists, the user is logged in and if they try to log in we redirect them to the home page
return res.redirect('/');
}
I hope this helps.

Send data back with the Passport js failureRedirect method

I have a Passport js local signup strategy that makes use of the successRedirect and failureRedirect methods. The problem is, if there is an error with the signup process, Passport will simply redirect back to the signup form, with no data in the form.
app.post('/signup', passport.authenticate('local-signup', {
successRedirect: '/signup/avatar', // redirect to the secure profile section
failureRedirect: '/signup', // redirect back to the signup page if there is an error
failureFlash: true // allow flash messages
}));
I have the following condition inside my local signup strategy that will send a failure message to the user if the two provided passwords do not match.
if(req.body.password != req.body.confirm_password){
console.log('Passwords do not match');
return done(null, false, req.flash('signupMessage', 'Your passwords do not match'));
}
Along with the flash message, I want to send data back to the /signup route and re-populate the form with the data the user has provided.
Is there a way to send the form data back to the browser on failure?
The answer was much simpler than I expected.
The validation should happen before the data is sent to Passport js, then if the data is not valid, the /signup template can be re-rendered and the data passed back.
The solution was in this question: NodeJS express-validator with passport
Sorry, after our comments I understand what you're asking a little better now. You need to make use of a custom callback in your passport code. The passport site gives this example:
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);
});
Through a closure here, your callback has access to both the request and the response, so when you deal with your error state, you can invoke code to update the response from the originating request (e.g. pulling in the specified username or whatever it is you wish to re-populate the sign-up screen with).
http://passportjs.org/docs/authenticate

NodeJS + Passport.js - no session stored?

I have a problem with sessions in my app. I'm trying to learn Passport.js based on this tutorial: http://www.sitepoint.com/local-authentication-using-passport-node-js/ . What I want to do is to allow the acccess for only authenticated users. The process of login works great, but when I check if the user is authenticated, it always says not. What could go wrong?
Here is the checking function:
if (req.isAuthenticated()) {
return next();
else {
res.redirect('/');
}
Here is the path from the router:
router.get('/secret', isAuthenticated, function(req, res) {
res.send('Welcome to the secret page');
});
I didn't find any domunentation about how to check if the session was established, where it is and so on.
Try this, taken from passport.js documentation.
app.get('/secret', passport.authenticate('local'), function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
});
http://passportjs.org/guide/authenticate/

Cookie detection inbetween Express and Passport

Users visiting http://localhost/login are instantly redirected to Facebook for confirmation of application usage. Once authorized, Facebook contacts http://localhost/login?code= with a special code that allows the server to obtain the user's information such as their name and gender.
Express().get('/login', Passport().authenticate('facebook', {
failureRedirect: 'http://localhost/',
}), function(req, res) {
database.saveData(req.user, **randomlyGeneratedHash**);
res.cookie('session', **randomlyGeneratedHash**);
res.end();
});
This works as expected, but when authenticated users visit the /login in succession, the whole process is repeated and they get a new cookie.
Is there a way that I can run some code inbetween Express and Passport, to stop Passport from redirecting to Facebook if the user has a valid cookie already?
You can use something similar to ensureAuthenticated on your /login route:
var CheckIfAlreadyLoggedIn = function(req, res, next) {
if (req.isAuthenticated()) {
return res.redirect('/'); // or where you want them to go
}
next();
};
Express().get('/login', CheckIfAlreadyLoggedIn, Passport().authenticate('facebook', ...));
This would redirect users that are already logged in back to / when they try to access /login.

accessing session variables in redirected file in node.js

I am building a crappy login system as a newbie. I have done this so far:
app.post("/verifyLogin",function(request,response){
var usr=request.body.username;
var pass=request.body.password;
userModel.find({$and:[{username:usr},{password:pass}]},function(err,user){
if(user.length==0)
{
response.redirect("/?msg=failed");
}
else
{
request.session.user=user;
response.redirect("/dashboard");
}
});
});
This works fine but after successful login i want to get the user details in the dashboard. I am clueless. Please shed some light.
EDIT
I have the following setup for dashboard in routes:
app.get("/dashboard",function(request,response){
response.sendfile('/lms/site/dashboard.html');
});
If you mean you want to pass the users' details to a template:
app.get('/dashboard', function(req, res) {
res.render('dashboard', {
user : req.session.user
});
});
This assumes a few things though:
you have a working templating setup;
you have a template called dashboard (with an extension matching your templating setup);
you're going to provide some sort of setup to make sure a user is logged in before they can open /dashboard.
EDIT: since you don't want to use templating, you could use AJAX to get the user details from the server into the client:
// server side
app.get('/userdata', function(req, res) {
// check if a user is logged in
...
// return the user details as JSON
res.send(req.session.user);
});
// client side (in 'dashboard.html', this assumes is will load jQuery)
$.getJSON('/userdata', function(user) {
// process user data, insert it into the DOM somewhere...
});
EDIT 2: to check if a user is logged in, you could create a middleware which would check for the existence of req.session.user and redirect to the login page if it's undefined:
var isLoggedIn = function(req, res, next) {
if (req.session && req.session.user)
next(); // user logged in, so pass
else
res.redirect('/'); // not logged in, redirect to login page
};
You would use the isLoggedIn middleware for all routes that require a user to be logged in:
app.get('/userdata', isLoggedIn, function(req, res) {
res.send(req.session.user);
});

Resources