I have been banging my head against the wall for the past couple of days trying to fix this issue. Basically, I added passport to my React app for user authentication, and I want to be able to check if the user has been added to the session from a couple of components to ensure the user is logged in.
When I post the email / password to the ‘/login’ route it serializes the user, but when I redirect to the home page the user is no longer in the session. It appears that the deserialize function isn’t running at all.
Below is the passport setup and my user routes:
passport.js
const mongoose = require('mongoose');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const Users = require('../models/Users');
passport.serializeUser((user, done) => {
console.log('Serialized user');
done(null, {_id: user._id});
})
passport.deserializeUser((id, done) => {
console.log("Deserializing user...");
Users.findOne(
{_id: id},
(err, user) => {
console.log("Deserialized user ");
done(null, user);
}
)
})
passport.use( new LocalStrategy(
{
usernameField: 'user[email]',
passwordField: 'user[password]'
},
function(email, password, done){
Users.findOne({email: email}, (err, user) => {
if(err) {
done(err);
}
if(!user) {
return done(null, false, { message: "Incorrect email"})
}
if(!user.validatePassword(password)){
return done(null, false, { message: "Invalid password" });
}
return done(null, user);
})
}
));
module.exports = passport;
user.js (user routes)
const mongoose = require('mongoose');
const passport = require('../../config/passport');
const userRouter = require('express').Router();
const auth = require('./auth');
const Users = require('../../models/Users');
userRouter.post('/', auth.optional, function(req, res){
const user = req.body.user;
if(!user.email) {
return res.status(422).json({
errors: {
email: 'is required'
}
})
}
if(!user.password) {
return res.status(422).json({
errors: {
password: 'is required'
}
})
}
const finalUser = new Users(user);
finalUser.setPassword(user.password);
return finalUser.save()
.then(() => res.json({ user: finalUser.toAuthJSON() }));
})
userRouter.post('/login', auth.optional, (req, res, next) => {
const {body: {user}} = req;
if(!user.email) {
return res.status(422).json({
errors: {
email: 'is required'
}
})
}
if(!user.password) {
return res.status(422).json({
errors: {
password: 'is required'
}
})
}
next();
},
passport.authenticate('local'), (req, res) => {
if(req.user) {
const user = req.user;
user.token = user.generateJWT();
return res.status(200).json({user: {
id: user._id,
email: user.email,
token: user.token
}});
}
return res.sendStatus(404);
}
);
userRouter.get('/current', auth.required, (req, res, next) => {
const { payload: {id}} = req;
return Users.findById(id)
.then((user) => {
if(!user) {
return res.sendStatus(400);
}
console.log(req.session);
return res.json({ user: user.toAuthJSON() });
})
});
userRouter.get('/checkLogin', auth.optional, (req, res, next) => {
console.log(req.session)
if(req.session.passport){
const user = req.session.passport.user;
return res.json(user);
}
return res.sendStatus(404);
})
module.exports = userRouter;
This is the code to setup my express server:
require('dotenv').config()
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var cors = require('cors');
var mongoose = require('mongoose');
var session = require('express-session');
var bodyParser = require('body-parser');
var passport = require('./config/passport');
var indexRouter = require('./routes/index');
var newRouter = require('./routes/new');
var articleRouter = require('./routes/article');
var deleteRouter = require('./routes/delete');
var userRouter = require('./routes/api/users');
var app = express();
const PORT = process.env.PORT || 4001;
mongoose.promise = global.Promise;
//connect to database
mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
}).then(() => {
console.log('Database sucessfully connected')
},
error => {
console.log('Database could not be connected: ' + error)
}
)
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser('coder-session'));
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(session({
secret: 'coder-session',
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use('/', indexRouter);
app.use('/new', newRouter);
app.use('/article', articleRouter);
app.use('/delete', deleteRouter);
app.use('/users', userRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(PORT, () => {
console.log("Listening on port " + PORT);
})
module.exports = app;
Also my code forthe login form:
import React from 'react';
import '../../resources/css/LoginForm.css';
import {Redirect} from 'react-router-dom';
export class LoginForm extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {},
redirect: false,
returnedUser: {}
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
const user = this.state.user;
user[e.target.name] = e.target.value;
this.setState({
user: user
})
}
handleSubmit(e) {
e.preventDefault();
const user = {
user: this.state.user
}
fetch("http://localhost:4001/users/login", {
method: 'post',
body: JSON.stringify(user),
headers: {
"Content-Type": "application/json"
}
})
.then(response => response.json())
.then(jsonResponse => {
if(jsonResponse.errors){
const error = "*" + Object.keys(jsonResponse.errors)[0] + " " + Object.values(jsonResponse.errors)[0];
document.getElementById("error-message").innerText = error;
document.getElementById(Object.keys(jsonResponse.errors)[0]).style.border = 'red';
}
else {
console.log(jsonResponse);
this.setState({
redirect: true,
returnedUser: jsonResponse.user
})
}
});
}
render() {
if(this.state.redirect){
return (<Redirect to={{
pathname: "/",
state: { loggedIn: true, user: this.state.returnedUser }
}} />)
}
else {
return (
<div className="Form">
<form id="login-form" onSubmit={this.handleSubmit}>
<div className="input-field">
<label htmlFor="email">Email: </label>
<input id="email" name="email" type="text" value={this.state.email} onChange={this.handleChange}/>
</div>
<div className="input-field">
<label htmlFor="password">Password: </label>
<input id="password" name="password" type="password" value={this.state.password}
onChange={this.handleChange}/>
</div>
<div id="error-message"></div>
<input type="submit" value="Login" className="button" />
</form>
</div>
);
}
}
}