Skip to content
Advertisement

Redirecting to previous page in Passport.js after authentication (not duplicate) Node.js

I am trying to redirect back to the page when I am redirected to log in. I am using Passport and connect-ensure-login, and the login works, however, it gets annoying having to re-click the link (if you are not logged in, it redirects you to the home page, but the link has a query string). Is there a way to add a redirect URL upon successful log-in?

The redirect URL needs to be dynamic, because it is query strings, and is not dependent upon the user’s id or anything like that.

Here is a snippet of my server-side code (please tell me if you need any other snippets because my app.js is 224 lines long and I don’t want to post that). app.js:

let express = require( 'express' );
let app = express();
const expressSession = require('express-session')({
    secret: `secret`,
    resave: false,
    saveUninitialized: false
});
  
app.use(expressSession);
const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
let path = require( 'path' );
const passportLocalMongoose = require('passport-local-mongoose');
const connectEnsureLogin = require('connect-ensure-login');
var mongoose = require("mongoose");
var bodyParser = require("body-parser");

const Mixed = mongoose.Schema.Types.Mixed;
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/db', { useNewUrlParser: true, useUnifiedTopology: false });

const Schema = mongoose.Schema;
const UserDetail = new Schema({
  username: String,
  email: Mixed,
  password: String,
  verified: Boolean
});
UserDetail.plugin(passportLocalMongoose);
const UserDetails = mongoose.model('userInfo', UserDetail, 'userInfo');

passport.use(UserDetails.createStrategy());

passport.serializeUser(UserDetails.serializeUser());
passport.deserializeUser(UserDetails.deserializeUser());

app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }));

app.use( '/login', express.static( path.join( __dirname, 'login' ) ) );

app.get( '/', connectEnsureLogin.ensureLoggedIn(), ( req, res ) => {
    res.sendFile( __dirname + '/index.html' );
});

app.post('/login', (req, res, next) => {
    passport.authenticate('local',
    (err, user, info) => {
      if (err) {
        return next(err);
      }
  
      if (!user) { 
        return res.redirect('/login?info=' + info); 
      }
  
      req.logIn(user, function(err) {
        if (err) {
          return next(err);
        }
  
        return res.redirect('/');
      });
  
    })(req, res, next);
});
  

app.get('/login', (req, res) => {
    res.sendFile('login/login.html', { root: __dirname })
});
app.get('/register', (req, res) => {
    res.sendFile('login/register.html', { root: __dirname })
});
app.get('/user', connectEnsureLogin.ensureLoggedIn(), (req, res) => {
    res.send({user: req.user})
})
  
app.post('/register', (req, res) =>{
    UserDetails.register({ username: req.body.username, active: false, email: req.body.email }, req.body.password)
    res.redirect('/login')
})

Is there a way to:

  1. Save query string in the session when using connectEnsureLogin.ensureLoggedIn()?
  2. Or save the query string in another way and read that when you go to redirect?

I’m new to Node.js so I followed an example to get the Login to work (sorry, I do not have the example now, I don’t know where I found it and I didn’t save a copy of it).

Also, this question does not duplicate this SO question because I cannot think of how to implement the answer with my current middleware, connectEnsureLogin.ensureLoggedIn(). If not, please tell me how I can implement @Chovy’s answer to use my current situation.

Thanks so much!!!!

Edit:

I think it has something to do with redirectTo in connectEnsureLogin, however, I cannot get it to read the query string. I tried to set redirectTo to req.url but my servers errors with:

    returnTo: req.query
              ^

ReferenceError: req is not defined

Is there a way to do this? Thanks

Advertisement

Answer

Sadly, connectEnsureLogin does not provide the customizations needed in your case, however, a simple middleware function does exactly what you want.

At the top, define a middleware function and set a session cookie called RedirectTo:

function auth (req, res, next) {
    if (req.isAuthenticated()) { 
        // user is authenticated, so we do not need to redirect them.
        return next();
    } else {
        // user is not authenticated, so add a session cookie telling our server where to redirect to.
        req.session.redirectTo = req.url;
        // redirect to login with cookie set
        res.redirect('/login');
    }
}

You can use the middleware like this:

app.get( '/', auth, ( req, res ) => {
    //        ^^^^ notice that we use our middleware instead
    res.sendFile( __dirname + '/index.html' );
})

Then, you can read that session cookie in your post request handler like this:

app.post('/login', (req, res, next) => {
    // authentication like normal
    passport.authenticate('local',(err, user, info) => {
      if (err) {
        return next(err);
      }
  
      if (!user) { 
        return res.redirect('/login?info=' + info); 
      }
  
      req.logIn(user, function(err) {
        if (err) {
          return next(err);
        }
        // this is the special part
        // redirect the user to the session cookie if it exists
        var redirectTo = req.session.redirectTo || '/';
        // delete the session cookie so it is not present on the next request
        delete req.session.redirectTo;
        // redirecting the user to where they want to go
        res.redirect(redirectTo || '/');
      });
  
    })(req, res, next);
});
User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement