javascriptnode.jsexpresspassport.jspassport-local

Can't get Passport.js redirect and flash message to work


I'm a beginner with all things Node and trying to configure a simple passport-local strategy for my app. I was able to create a new user in my database, but beyond that I'm having trouble getting passport.js to work as expected.

Here's my express app structure:

routes
  - api
    - events.js
    - users.js
services
  - passport.js
server.js

In server.js I'm configuring the routes individually, like app.use('/api/users', require('./routes/api/users'). Just before that I'm doing the following passport setup:

const session = require('express-session');
const cookieParser = require('cookie-parser');
const passport = require('passport');
const flash = require('connect-flash');

require('./services/passport')(passport);

app.use(session({ secret: process.env.SESSION_SECRET,
                  resave: true,
                  saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());

My passport.js file looks like this:

const knex = require('../db/knex.js');
const LocalStrategy   = require('passport-local').Strategy;
const bcrypt = require('bcryptjs');
const saltRounds = 12;

module.exports = function(passport) {

    passport.serializeUser(function(user, done) {
        done(null, user);
    });

    passport.deserializeUser(function(user, done) {
        done(null, user);
    });

    passport.use('local-signup', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done) {

        knex('users')
        .where({email: email}).first()
        .then(function(user) {
            if (user) { return done(null, false, {message: 'User with that email already exists!'}) }
            else {
                bcrypt.hash(password, saltRounds, function(err, hash) {
                    var newUser = new Object();
                    newUser.username = req.body.username;
                    newUser.email = email;
                    newUser.password = hash;

                    knex('users')
                        .insert(newUser)
                        .then(done(null, newUser))
                        .catch(err => { return done(err) })
                })
            }
        })
        .catch(err => { return done(err) });
    }));
};

And then in users.js I have:

const express = require('express');
const router = express.Router();
const knex = require('../../db/knex.js');
const passport = require('passport');
...
router.post('/signup', passport.authenticate('local-signup', {
    successRedirect: '/',
    failureRedirect: '/',
    failureFlash: true
}));

I'll eventually want to make successRedirect and failureRedirect different from each other, of course, but for now I just want to understand how to make them work. As I mentioned, this code successfully inserted a user into the database. But when I try to create another account with the same credentials, nothing happens. The behavior I'd expect is to see the message "User with that email already exists!" and be redirected to my homepage.

Note that my homepage is at http://localhost:3000, so I'm not sure if setting the redirect to '/' points there or to the current page. The current page is http://localhost:3000/auth, which contains a React component with my signup form, making a POST call to /api/users/signup/. So it's conceivable that '/' would actually look for http://localhost:3000/api/users/signup which as of now doesn't have a GET method. But I'm not getting a 404 or anything; nothing happens, and there's no error. Any advice greatly appreciated.

UPDATE
I just confirmed that removing both redirects causes a 401 response as expected. Also, setting the failureRedirect to /events produces a 404, which is odd because http://localhost:3000/events is definitely configured.

EDIT
I realized it might help to show how I'm making the API call on the client side. Pressing a submit button calls the following signup function:

signup(e) {
        e.preventDefault();
        axios.post('/api/users/signup', {
            username: this.state.username,
            email: this.state.email,
            password: this.state.password
        })
        .catch(err => console.log(err));
    }

Solution

  • Is your express backend just used as a data API? Because if it is then I'm not sure if the PassportJS redirection would work.

    I remember using it in a completely server side rendered app, where the routing was completely on backend, that kind of situation makes it work because any routing is handled on backend and it renders the page then sends it to client.

    On a react app, the routing (the actual change in page/url) is handled on frontend, so doing it in Passport like this won't work. I think the best you can do is have error handling in passport that sends some kind of status or message to frontend and then handle it on frontend because that's where the routing is.