James Monger
James Monger

Reputation: 10665

PassportJS and Express 4 not saving cookies / session correctly

I have an app using Express 4 with Passport 0.3.2. I have set up a passport-local strategy, which is getting the correct user information when the /session endpoint is sent a username and password.

However the user information is never saved correctly. As such req.user is always undefined in all listeners and req.isAuthenticated() always returns false.

I have seen other posts which often find issues with the ordering of the middleware setup however I have ordered them in the correct way and so I am not sure where to go from here.

Here is my POST listener for /session:

app.post("/session",
    passport.authenticate('local'),
    (req: any, res: any) => {
        // if we reach this point, we authenticated correctly
        res.sendStatus(201);
    }
);

Here is my LocalStrategy setup:

passport.use(new LocalStrategy(
    (username, password, done) => {
        let users = userRepository.getAll();

        let usernameFilter = users.filter(u => u.getUsername() === username);
        if (!usernameFilter || usernameFilter.length !== 1) {
            return done(null, false, { message: 'Incorrect username.' });
        }

        if (!password || password !== "correct") {
            return done(null, false, { message: 'Incorrect password.' });
        }

        return done(null, usernameFilter[0]);
    }
));

Here is my app setup:

let app = express();
app.use(cookieParser());
app.use(bodyParser.json());
app.use(expressSession({
    secret: 'my secret key',
    resave: true,
    saveUninitialized: true
}));
app.use(passport.initialize());
app.use(passport.session());

I am using the following dependency versions:

"body-parser": "^1.15.1",
"cookie-parser": "^1.4.3",
"express": "^4.13.4",
"express-session": "^1.13.0",
"passport": "^0.3.2",
"passport-local": "^1.0.0"

I have added a callback to my POST /session, however an error is thrown. This is my callback:

app.post("/session",
    passport.authenticate('local', {
      session: false
    }),
    (req: express.Request, res: express.Response) => {
        req.logIn(req.user, (err: any) => {
            if (err)
                throw err;
        });

        // if we reach this point, we authenticated correctly
        res.sendStatus(201);
    }
);

I get the following error thrown:

Error: Failed to serialize user into session

Upvotes: 6

Views: 8425

Answers (4)

lesterfernandez
lesterfernandez

Reputation: 505

Cookie-parser and body-parser are not necessary for this. Just express-session and passport.

It seems to me like your cookies are not being sent or received because of your express-session configuration (or lack thereof)

In order to consume cross-site cookies, the sameSite property needs to be set to none. However, if the sameSite property is set to none, the secure property is required to be set to true. Which will make it so the cookie is only sent through HTTPS. This doesn't work when developing on http://localhost.

To get around this, while in development sameSite can be set to "lax" and secure can be set to "auto".

const inProd = process.env.NODE_ENV === "production";

app.use(
  session({
    secret: process.env.COOKIE_SECRET,
    resave: true, // resave cookies even if nothing changed
    saveUninitialized: false,
    cookie: {
      sameSite: `${inProd ? "none" : "lax"}`, // cross site // set lax while working with http:localhost, but none when in prod
      secure: `${inProd ? "true" : "auto"}`, // only https // auto when in development, true when in prod
      maxAge: 1000 * 60 * 60 * 24 * 14, // expiration time
    },
  })
);

Upvotes: 2

Rov
Rov

Reputation: 133

After looking at @JamesMonger's question and @pulse0ne's answer, I figured out how to fix my problem. My session was not getting created as well in react with passport and cookie-session. I am sharing the problem code and the solution here, so that someone can benefit from it. Old code that did not work

server.get('/auth/google/callback', passport.authenticate('google'),
(req, res) => {
     res.redirect('/api/current_user');
});

After adding session: true, the session got created and I was able to retrieve it from the request.

server.get('/auth/google/callback', passport.authenticate('google', {session: true}),
(req, res) => {
      res.redirect('/api/current_user');
});

Versions:

    "body-parser": "^1.19.0",
    "cookie-session": "^1.3.3",
    "express": "^4.16.4",
    "passport": "^0.4.0",

Upvotes: 1

Tom Söderlund
Tom Söderlund

Reputation: 4747

I found this question from a Google search when I had a related problem.

What I realized was that express-session doesn’t persist sessions e.g. between server restarts. Read here for more info.

So I swapped express-session for cookie-session instead:

app.use(cookieSession({
    name: 'MyAppName',
    keys: ['very secret key'],
    maxAge: 30 * 24 * 60 * 60 * 1000 // 30 days
}));

And then PassportJS was persisting my sessions, no further change needed!

Upvotes: 7

pulse0ne
pulse0ne

Reputation: 1102

From the passport docs:

Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.


Ah, good, we're making progress. You'll have to configure passport to serialize and deserialize the user information. See this for information. It should be noted that you will replace User with your own database access object.

Upvotes: 4

Related Questions