117

I am using node with express + mongoose and trying to use passport.js with restful api.
I keep getting this exception after authentication success (I see the callback url on the browser):

/Users/naorye/dev/naorye/myproj/node_modules/mongoose/lib/utils.js:419
        throw err;
              ^
Error: passport.initialize() middleware not in use
    at IncomingMessage.req.login.req.logIn (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/http/request.js:30:30)
    at Context.module.exports.delegate.success (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/middleware/authenticate.js:194:13)
    at Context.actions.success (/Users/naorye/dev/naorye/myproj/node_modules/passport/lib/passport/context/http/actions.js:21:25)
    at verified (/Users/naorye/dev/naorye/myproj/node_modules/passport-facebook/node_modules/passport-oauth/lib/passport-oauth/strategies/oauth2.js:133:18)
    at Promise.module.exports.passport.use.GitHubStrategy.clientID (/Users/naorye/dev/naorye/myproj/config/passport.js:91:24)
    at Promise.onResolve (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:162:8)
    at Promise.EventEmitter.emit (events.js:96:17)
    at Promise.emit (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:79:38)
    at Promise.fulfill (/Users/naorye/dev/naorye/myproj/node_modules/mongoose/node_modules/mpromise/lib/promise.js:92:20)
    at /Users/naorye/dev/naorye/myproj/node_modules/mongoose/lib/query.js:1822:13

I have read that I should put app.use(passport.initialize()); and app.use(passport.session()); before app.use(app.router); and this is what I did. Here is my express.js that registers the middlewares:

var express = require('express'),
    mongoStore = require('connect-mongo')(express),
    flash = require('connect-flash'),
    helpers = require('view-helpers');

module.exports = function (app, config, passport) {
    app.set('showStackError', true);
    // should be placed before express.static
    app.use(express.compress({
        filter: function (req, res) {
            return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
        },
        level: 9
    }));
    app.use(express.favicon());
    app.use(express.static(config.root + '/public'));

    app.use(express.logger('dev'));

    // set views path, template engine and default layout
    app.set('views', config.root + '/app/views');
    app.set('view engine', 'jade');

    app.configure(function () {
        // use passport session
        app.use(passport.initialize());
        app.use(passport.session());

        // dynamic helpers
        app.use(helpers(config.app.name));

        // cookieParser should be above session
        app.use(express.cookieParser());

        // bodyParser should be above methodOverride
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        // express/mongo session storage
        app.use(express.session({
            secret: 'linkit',
            store: new mongoStore({
                url: config.db,
                collection : 'sessions'
            })
        }));

        // connect flash for flash messages
        app.use(flash());

        // routes should be at the last
        app.use(app.router);

        // assume "not found" in the error msgs
        // is a 404. this is somewhat silly, but
        // valid, you can do whatever you like, set
        // properties, use instanceof etc.
        app.use(function(err, req, res, next){
            // treat as 404
            if (~err.message.indexOf('not found')) {
                return next();
            }

            // log it
            console.error(err.stack);

            // error page
            res.status(500).render('500', { error: err.stack });
        });

        // assume 404 since no middleware responded
        app.use(function(req, res, next){
            res.status(404).render('404', {
                url: req.originalUrl,
                error: 'Not found'
            });
        });
    });
};

What is wrong?

UPDATE According to @Peter Lyons I have changed the configurations order to the following, but I still got the same error:

var express = require('express'),
    mongoStore = require('connect-mongo')(express),
    flash = require('connect-flash'),
    helpers = require('view-helpers');

module.exports = function (app, config, passport) {
    app.set('showStackError', true);
    // should be placed before express.static
    app.use(express.compress({
        filter: function (req, res) {
            return /json|text|javascript|css/.test(res.getHeader('Content-Type'));
        },
        level: 9
    }));
    app.use(express.favicon());
    app.use(express.static(config.root + '/public'));

    app.use(express.logger('dev'));

    // set views path, template engine and default layout
    app.set('views', config.root + '/app/views');
    app.set('view engine', 'jade');

    app.configure(function () {

        // dynamic helpers
        app.use(helpers(config.app.name));

        // cookieParser should be above session
        app.use(express.cookieParser());

        // bodyParser should be above methodOverride
        app.use(express.bodyParser());
        app.use(express.methodOverride());

        // express/mongo session storage
        app.use(express.session({
            secret: 'linkit',
            store: new mongoStore({
                url: config.db,
                collection : 'sessions'
            })
        }));

        // connect flash for flash messages
        app.use(flash());

        // use passport session
        app.use(passport.initialize());
        app.use(passport.session());

        // routes should be at the last
        app.use(app.router);

        // assume "not found" in the error msgs
        // is a 404. this is somewhat silly, but
        // valid, you can do whatever you like, set
        // properties, use instanceof etc.
        app.use(function(err, req, res, next){
            // treat as 404
            if (~err.message.indexOf('not found')) {
                return next();
            }

            // log it
            console.error(err.stack);

            // error page
            res.status(500).render('500', { error: err.stack });
        });

        // assume 404 since no middleware responded
        app.use(function(req, res, next){
            res.status(404).render('404', {
                url: req.originalUrl,
                error: 'Not found'
            });
        });
    });
};
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Naor
  • 23,465
  • 48
  • 152
  • 268
  • Express 4.x versions do not support some methods. See https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x – miksiii Jan 21 '15 at 18:14

8 Answers8

229

Follow the example to avoid the out-of-order middleware hell that express makes it so easy to enter. Straight from the docs. Note how yours does not match this exactly.

var app = express();
app.use(require('serve-static')(__dirname + '/../../public'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({
  secret: 'keyboard cat',
  resave: true,
  saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());

Docs

  1. cookieParser
  2. session
  3. passport.initialize
  4. passport.session
  5. app.router

You

  1. passport.initialize
  2. passport.session
  3. cookieParser
  4. session
  5. app.router
Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • I've changed it to what you suggest but it still throws this error. I updated my question with the new express.js file/ – Naor May 28 '13 at 06:17
  • 7
    So the code you have here isn't your top-level code. Earlier in your program are you making any calls to `app.get`, `app.post`, etc? Those will cause the router to be added to the stack earlier than you intend. Show us ALL the revelant code starting with when you invoke the `express()` function to get your `app` object. That's my 2nd guess. – Peter Lyons May 28 '13 at 06:33
  • 3
    I have noticed that app.use(app.router); is called after passport initialize but I call: require('./config/routes')(app, passport, auth); before calling to express configurations. Switching between both lines solved the problem. Thanks! – Naor May 28 '13 at 08:26
  • 1
    This worked for me! But why does the middleware have to be in order like that? – Anthony To Apr 05 '14 at 02:49
  • 3
    By design so you can rely on prerequisites being done. Session won't work if cookieParser hasn't parsed the cookies yet. – Peter Lyons Apr 05 '14 at 04:01
  • Solved my problem. To anyone else looking who's using Express 4+, keep in mind some of those packages used are no longer included with Express and must be installed separately. The error messages are fairly good and explanatory so it should be a minor nuisance. – Alan Thomas Jan 14 '17 at 05:36
13

In my case (same error message) I've forgotten to add the passport initializations at all:

app.configure(function () {
    ...
    app.use(passport.initialize());
    app.use(passport.session());
});

UPDATE: Only working up to express version 3, version 4 does not support app.configure() anymore

Matthias M
  • 12,906
  • 17
  • 87
  • 116
  • 1
    App.configure cannot be used any more. https://github.com/strongloop/express/wiki/Migrating-from-3.x-to-4.x#removed-in-express-4.. They should update the passport docs. right? – jack blank Jan 07 '16 at 21:46
9

In my case the error was because I was trying to promisify req.login without binding this to req, so when the function was called it could not find passport settings. The solution is binding req.login.bind(req) before passing it to promisify if you are using Node v8.

Jiayi Hu
  • 2,210
  • 1
  • 14
  • 12
  • And this "scope issue" happens e.g. when you use arguments destructuring like `function({ login })` passing the `req` as first argument. Your solution worked for me, thanks – Manuel Di Iorio May 16 '18 at 10:17
  • Yup it's how `this` works in Javascript. If you don't call the function as object method then `this` will be `undefined` ( or `window` in the browser) – Jiayi Hu May 16 '18 at 10:21
  • Tip for anyone reading this answer and not understanding it... if you investigate `Function.prototype.call`, `Function.prototype.apply`, how `this` works in Javascript and the principles behind prototypical inheritance, you will promote to the level of Javascript Guru in the process :) – Stijn de Witt Dec 19 '18 at 22:13
  • Cheers, I was hoping it'd be as simple as `util.promisify(req.login.bind(req));` – Julian H. Lam Aug 15 '19 at 14:56
4

What has helped me also was to put routes AFTER cookies config:

// init Cookies:
app.use(
    cookieSession({
        maxAge: 30 * 24 * 60 * 60 * 1000,
        keys: [keys.cookieKey]
    })
);
app.use(passport.initialize());
app.use(passport.session());

// init routes
const authRoutes = require("./routes/authRoutes")(app);
Michał Dobi Dobrzański
  • 1,449
  • 1
  • 20
  • 19
3

Peter Lyons answer helped me to solve it, but i solved it in abit different way.

app.use(
  cookieSession({
    maxAge: 30 * 24 * 60 * 60 * 1000,
    keys: [keys.cookieKey],
  }),
);
app.use(passport.initialize());
app.use(passport.session());

Have a look at my GitHub repo for the whole code and not only the code snippet here.

Isak La Fleur
  • 4,428
  • 7
  • 34
  • 50
2

In my case (same error message), I was developing a custom strategy and I don't need to use a session. I just forgot to add session: false in my route authenticate middleware.

  app.post('/api/public/auth/google-token',
    passport.authenticate('google-token', {
      session: false
    }),
    function (req: any, res) {
      res.send("hello");
    }
  );
ruwan800
  • 1,567
  • 17
  • 21
1

Place the app.use(passport.initialize()) middleware in front of the app.router middle ware and it works like a charm

Arghya Sadhu
  • 41,002
  • 9
  • 78
  • 107
0

all you have to do is put this code before the router that your using like this

app.use('/users', usersRouter);//this is the router  I am using

//and this is the code you have to copy

app.use(passport.initialize());
app.use(passport.session());