简体   繁体   中英

Why are Cookies Not Sent on Requests in MERN Application using Axios

I'm trying to implement login using passport.js with express-session following the example here but I'm having trouble persisting logins. I notice the session cookie is not being sent on any route unless the route includes passport.authenticate('local') , which adds the cookie I guess. This means the /getUser route always returns no user, and a new session is added to the MongoDB store every time I login.

My routes are as follows:

router.get('/getUser', userController.getCurrentUser);
router.post('/register', userController.register, passport.authenticate('local'), authController.login);
router.post('/login', passport.authenticate('local'), authController.login);
router.post('/logout', authController.logout);

Controlling functions are:

exports.getCurrentUser = (req, res) => {
  if (req.user) {
    return res.send(req.user);
  } else {
    res.json({ error: 'No user found' });
  };
};

exports.register = async (req, res, next) => {
  const user = new User({ 
    email: req.body.email, 
    name: req.body.name,
  });
  const register = promisify(User.register, User);
  await register(user, req.body.password);
  next();
};

exports.login = (req, res) => {
  req.login(req.user, function(err) {
    if (err) { res.json({ error: err }); }
    return res.send(req.user);
  });
};

exports.logout = (req, res) => {
  if (req.user) {
    req.logout();
    res.send({ msg: 'logged out' });
  } else {
    res.send({ msg: 'no user to log out' })
  };
};

User model is:

const mongoose = require('mongoose')
const Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
const validator = require('validator');
const passportLocalMongoose = require('passport-local-mongoose');

const userSchema = new Schema({
  email: {
    type: String,
    unique: true,
    lowercase: true,
    trim: true,
    validate: [validator.isEmail, 'Please enter a valid email'],
    required: 'Please enter an email address'
  },
  name: {
    type: String,
    required: 'Please enter a name',
    trim: true
  },
  resetPasswordToken: String,
  resetPasswordExpires: Date,
  isAdmin: {
    type: Boolean,
    default: false
  },
}, { timestamps: true });

userSchema.plugin(passportLocalMongoose, { usernameField: 'email' });

module.exports = mongoose.model('User', userSchema);

Axios module in the FE:

import axios from 'axios';

const api = axios.create({
  baseURL: 'http://localhost:8000/api',
});

export const getUser = () => api.get('/getUser');
export const register = payload => api.post('/register', payload);
export const login = payload => api.post('/login', payload);
export const logout = payload => api.post('/logout', payload);

const apis = {
  getUser,
  register,
  login,
  logout,
};

export default apis;

Passport config is:

const mongoose = require('mongoose');
const User = mongoose.model('User');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;

passport.serializeUser((user, done) => {
    done(null, { _id: user._id });
});

passport.deserializeUser((id, done) => {
    User.findOne({ _id: id },   'username', (err, user) => {
        done(null, user)
    });
});

const strategy = new LocalStrategy(
    {   usernameField: 'email'  },
    function(email, password, done) {
        User.findOne({ email: email }, (err, user) => {
            if (err) {
                return done(err);
            }
            return done(null, user);
        });
    }
);

passport.use(strategy);

module.exports = passport;

And finally server config is:

const express = require('express');
const session = require('express-session');
const mongoose = require('mongoose');
const MongoStore = require('connect-mongo')(session);
const cors = require('cors');
const bodyParser = require('body-parser');
const promisify = require('es6-promisify');
//  import all models
require('./models/User');

const passport = require('./handlers/passport');

const db = require('./database');
const router = require('./routes');

const app = express();

//  enable CORS for all origins to allow development with local server
app.use(cors({credentials: true}));

// use bodyParser to allow req.params and req.query
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use(session({
  secret: process.env.SECRET,
  key: process.env.KEY,
  resave: false,
  saveUninitialized: false,
  store: new MongoStore({ mongooseConnection: mongoose.connection })
}));

// passport.js to handle logins
app.use(passport.initialize());
app.use(passport.session());

// pass variables on all requests
app.use((req, res, next) => {
  res.locals.user = req.user || null;
  res.locals.session = req.session;
  next();
});

// promisify some callback based APIs
app.use((req, res, next) => {
  req.login = promisify(req.login, req);
  next();
});

app.use('/api', router);

app.get('/', (req, res) => {
    res.send('This is the Hely Cosmetics website backend/API');
})

app.set('port', process.env.PORT || 8000);
const server = app.listen(app.get('port'), () => {
  console.log(`Express running on port ${server.address().port}`);
});

I thought it might be cors but I tried app.use(cors({credentials: true})); with the same result, and verified the same issues with both the frontend and backend deployed as Heroku apps.

Full source code is available here .

What am I missing?

From a glance at the full source code, it doesn't look like you've set withCredentials: true in the Axios requests you're making to your backend. Passport, at a high level, works like this:

  1. Your /login and /register routes send a cookie back in their response. This is stored by your browser for future requests to identify the session (which, by your comment, seem to be working correctly).

  2. When you want to make an authenticated request to your backend, Axios has to explicitly send the cookie back in the request if it's cross-origin — that's where withCredentials comes in. Without it, Axios isn't sending the cookie with the request to identify the session.

By setting withCredentials: true , Axios should send the cookie back in the requests and Passport & express-session will use it to identify the user.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM