Hashnode / mern-starter

⛔️ DEPRECATED - Boilerplate for getting started with MERN stack
MIT License
5.15k stars 1.18k forks source link

Integration with Passport - Multiple Requests #384

Closed judylin1 closed 5 years ago

judylin1 commented 6 years ago

I'm trying to add Passport.JS to my MERN project, but deserializeUser keeps getting called twice. Both times I can see my user object, but the first time req.user is undefined, but the second time it's correct. Why is it getting called twice and why does only the second time work? Does anyone have a working example with Passport?

Additional details: MERN: 2.0.0 passport: 0.4.0 express-session: 1.15.6

Userobject from both deserializeUser calls: { userId: '1234', firstName: 'Joe', lastName: 'Smith', email: 'asdf@aol.com' }

Server.js: `import Express from 'express'; import compression from 'compression'; import bodyParser from 'body-parser'; import path from 'path'; const passport = require('passport'); const cookieParser = require('cookie-parser'); const expressSession = require('express-session'); const MongoStore = require('connect-mongo')(expressSession);

// Webpack Requirements import webpack from 'webpack'; import config from '../webpack.config.dev'; import webpackDevMiddleware from 'webpack-dev-middleware'; import webpackHotMiddleware from 'webpack-hot-middleware';

// Initialize the Express App const app = new Express();

// Set Development modes checks const isDevMode = process.env.NODE_ENV === 'development' || false; const isProdMode = process.env.NODE_ENV === 'production' || false;

// Run Webpack dev server in development mode if (isDevMode) { const compiler = webpack(config); app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath })); app.use(webpackHotMiddleware(compiler)); }

// React And Redux Setup import { configureStore } from '../client/store'; import { Provider } from 'react-redux'; import React from 'react'; import { renderToString } from 'react-dom/server'; import { match, RouterContext } from 'react-router'; import Helmet from 'react-helmet';

import routes from '../client/routes'; import { fetchComponentData } from './util/fetchData'; import serverConfig from './config'; import serverRoutes from './routes/routes';

app.use(compression()); app.use(Express.static(path.resolve(__dirname, '../dist/client'))); app.use(cookieParser('somethingsomething')); app.use(bodyParser.json({ limit: '20mb' })); app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }));

require('./passportConfig')(passport); app.use(expressSession({ name: 'testApp', secret: 'somethingsomething', resave: true, saveUninitialized: false, store: new MongoStore({ url: process.env.DBURL }), cookie: { maxAge: 36000 24 14, secure: false, }, })); app.use(passport.initialize()); app.use(passport.session()); app.use((req, res, next) => { res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept'); next(); });

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

// Render Initial HTML const renderFullPage = (html, initialState) => { const head = Helmet.rewind();

// Import Manifests const assetsManifest = process.env.webpackAssets && JSON.parse(process.env.webpackAssets); const chunkManifest = process.env.webpackChunkAssets && JSON.parse(process.env.webpackChunkAssets);

return ` <!doctype html>

${head.base.toString()} ${head.title.toString()} ${head.meta.toString()} ${head.link.toString()} ${head.script.toString()} ${isProdMode ? `` : ''}
${html}

`; };

const renderError = err => { const softTab = ' '; const errTrace = isProdMode ? :<br><br><pre style="color:red">${softTab}${err.stack.replace(/\n/g,
${softTab})}</pre> : ''; return renderFullPage(Server Error${errTrace}, {}); };

// Server Side Rendering based on routes matched by React-router. app.use((req, res, next) => { match({ routes, location: req.url }, (err, redirectLocation, renderProps) => { if (err) { return res.status(500).end(renderError(err)); }

if (redirectLocation) {
  return res.redirect(302, redirectLocation.pathname + redirectLocation.search);
}

if (!renderProps) {
  return next();
}

const store = configureStore();

return fetchComponentData(store, renderProps.components, renderProps.params)
  .then(() => {
    const initialView = renderToString(
      <Provider store={store}>
        <RouterContext {...renderProps} />
      </Provider>
    );
    const finalState = store.getState();

    res
      .set('Content-Type', 'text/html')
      .status(200)
      .end(renderFullPage(initialView, finalState));
  })
  .catch((error) => next(error));

}); });

// start app app.listen(serverConfig.port, (error) => { if (!error) { console.log(MERN is running on port: ${serverConfig.port}! Build something amazing!); // eslint-disable-line } });

export default app;`

PassportConfig.js: `const LocalStrategy = require('passport-local').Strategy; const request = require('request'); const apiUrl = process.env.API_URL; const token = process.env.TOKEN; const User = require('./models/user'); import Config from './config';

const API_URL = (typeof window === 'undefined' || process.env.NODE_ENV === 'test') ? process.env.BASE_URL || (http://localhost:${process.env.PORT || Config.port}/api) : '/api';

module.exports = (passport) => { passport.serializeUser((userObject, done) => { done(null, userObject); });

passport.deserializeUser((userObject, done) => { if (userObject.adminUser) done(null, userObject.adminUser); else done(null, userObject); });

passport.use('local-login', new LocalStrategy({ usernameField: 'email', passwordField: 'password', passReqToCallback: true, // allows us to pass back the entire request to the callback }, (req, email, password, done) => { const proj = { firstName: 1, lastName: 1, email: 1, }; User.findOneAsync({ email }, proj) .then(user => { const userData = { userId: user._id, firstName: user.firstName, lastName: user.lastName, email: user.email, }; return done(null, userData); }); }) );

passport.use('signUp', new LocalStrategy({ usernameField: 'email', passwordField: 'password', passReqToCallback: true, }, (req, email, password, done) => { request({ url: ${API_URL}/signIn, method: 'POST', json: { email, password, }, 'Content-Type': 'application/json', Accept: 'application/json', headers: { Authorization: token, }, }, (err, resp, body) => { const respBody = body || {}; if (err) return done(err);

if (!respBody.email) return done(null, false, { message: respBody.err });

return done(null, { user: respBody });

}); })); };`

mannyhenri commented 6 years ago

Could you point out in your code where your object is being serialized and are you using a promise? If not might be resolving your issue as your call to wherever you send the object to will resolve before completing the operation with a promise...might resolve this issue if I understand what is the problem correctly.