Reputation: 131
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));
}
Upvotes: 1
Views: 541
Reputation: 1504
For those still facing an issue with sessions resetting when using Passport, I spent hours tracing and debugging only to find out that Passport's authenticate()
resets the entire request session internally without so much as a warning. The documentation is just awful, but anyway...
The culprit is the SessionManager
part of the framework, as can be seen here
// this line below
req.session.regenerate(function(err) { ... });
Passing the option keepSessionInfo: true
to passport.authenticate
will preserve the session, i.e:
passport.authenticate('my-strategy', {
... ,
keepSessionInfo: true
})(req, res, next);
Upvotes: 0
Reputation: 860
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.
Upvotes: 1