Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Session is not correctly obtained from a storage, using express-session and passportjs

I'm using passport, express-session and connect-pg-simple.

The problem is that session is not correctly obtained from a storage, where it gets properly (i hope, but doubt) saved.

My setup is the same, as in many tutorials, I've found around.

server.js:

import express from 'express';
import next from 'next';
import bodyParser from 'body-parser';
import session from 'express-session';
import connectPgSimple from 'connect-pg-simple';
const sessionStorage = connectPgSimple(session);
import initAuthentication from '!/server/authentication';

const dev = process.env.NODE_ENV !== 'production'; // eslint-disable-line
const port = process.env.PORT || 3000; // eslint-disable-line
const app = next({ dev });

app.prepare()
.then(() => {
  const server = express();

  server.use(express.static('public'));

  /* Note Since version 1.5.0, the cookie-parser middleware no longer needs
     to be used for this module to work. This module now directly reads and writes
     cookies on req/res. Using cookie-parser may result in issues
     if the secret is not the same between this module and cookie-parser.
     https://www.npmjs.com/package/express-session */
  // server.use(cookieParser());

  server.use(bodyParser.json()); // for parsing application/json
  server.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
  server.use(session({
    secret: 'keyboard cat',
    store: new sessionStorage(),
    resave: false,
    saveUninitialized: false,
    cookie: {
      secure: false,
      maxAge: 30 * 24 * 60 * 60 * 1000
    }})
  );

  initAuthentication(server);
});

authentication.js:

import passport from 'passport';
import LocalStrategy from 'passport-local';
import bcrypt from 'bcrypt';
import {User} from '!/server/db';
import util from 'util';

User.prototype.validPassword = function(password) {
  return bcrypt.compareSync(password, this.passwordHash);
};

User.authenticate = (username, password, done) => {
  User.findOne({where: {nickname: username}}).then(user => {
    if (!user) {
      return done(null, false, { message: 'Неверное имя пользователя.' });
    }
    if (!user.validPassword(password)) {
      return done(null, false, { message: 'Неверный пароль.' });
    }
    return done(null, user);
  }).catch(err => {
    return done(err);
  });
};

export default function initAuthentication(server) {
  server.use(passport.initialize());
  server.use(passport.session());

  passport.use('local-signIn', new LocalStrategy(User.authenticate));

  passport.serializeUser((user, done) => {
    console.log('> serializeUser "'+user.nickname+'" ('+user.id+')');
    done(null, user.id);
  });
  passport.deserializeUser((id, done) => {
    console.log('> deserializeUser with id: ' + id)
    User.findById(id).then(user => {
      done(null, user);
    }).catch(error => {
      console.log('Error in passport.deserializeUser: ' + error);
      done(error, null);
    });
  });

  server.post('/user/login',
    passport.authenticate('local-signIn'),
    (req, res) => {
      // If this function gets called, authentication was successful.
      // `req.user` contains the authenticated user.
      console.log('Authenticated user "'+req.user.nickname+'"');
      req.login(req.user, (err)=> {
        if (err) console.log(err);
        console.log('\t Session established for user "'+req.user.nickname+'"');
        res.json(req.user);
      });
    }
  );

  function logAuthenticationStatus(req, res, next) {
    console.log('Authentication status:')
    console.log('\treq.cookies: ' + util.inspect(req.cookies));
    console.log('\treq.isAuthenticated: ', req.isAuthenticated());
    console.log('\treq.session: ', req.session);
    if (req.user) {
      console.log('\tLogged in as "'+req.user.nickname+'"');
    } else {
      console.log('\tNot logged in');
    }
    next();
  }

  server.use(logAuthenticationStatus);

  server.get('/logout', function(req, res){
    req.logout();
    res.redirect('/');
  });
}

The interesting part is debug output of 'express-session', thanks to amazing npm-package debug.

So, when a request to /user/login comes, with username=AntonAL, following happens:

Executing (default): SELECT "id", "nickname", "email", "password", "passwordHash", "lastLoggedIn", "createdAt", "updatedAt" FROM "users" AS "user" WHERE "user"."nickname" = 'AntonAL' LIMIT 1;
> serializeUser "AntonAL" (1)
Authenticated user "AntonAL"
> serializeUser "AntonAL" (1)
     Session established for user "AntonAL"
  express-session saving qiQfhyAvDDPD7muJLtGdZudKqMug0aAC +23ms
  express-session split response +0ms
  express-session set-cookie connect.sid=s%3AqiQfhyAvDDPD7muJLtGdZudKqMug0aAC.0wIcunkcEjhaUzs4H7w4uuv6u%2FBKXMROuAm6%2FG0vVQw; Path=/; Expires=Sat, 30 Sep 2017 10:55:31 GMT; HttpOnly +1ms
info: POST /user/login 200 50ms statusCode=200, url=/user/login, host=localhost:3000, content-type=application/json, origin=http://localhost:3000, accept-encoding=gzip, deflate, connection=keep-alive, accept=application/json, user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8, referer=http://localhost:3000/user/login, content-length=44, accept-language=ru, method=POST, httpVersion=1.1, originalUrl=/user/login, , responseTime=50, user=AntonAL

So, session is saved in db, let's check it:

SELECT * FROM session WHERE sid='qiQfhyAvDDPD7muJLtGdZudKqMug0aAC';`

sid                |                                                                     sess                                                                      |       expire        
----------------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------+---------------------
qiQfhyAvDDPD7muJLtGdZudKqMug0aAC | {"cookie":{"originalMaxAge":2592000000,"expires":"2017-09-30T10:55:31.514Z","secure":false,"httpOnly":true,"path":"/"},"passport":{"user":1}} | 2017-09-30 13:55:32
(1 row)

So far, so good.

Now, I'm requesting a home page of a website and getting following:

info: GET / 200 486ms statusCode=200, url=/, host=localhost:3000, accept-encoding=gzip, deflate, cookie=connect.sid=s%3AO6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE.M4iFzpVZP9fNa%2FLEomsMD8D9LjA1uFnDMnXT%2FHR3wyk; meteor_login_token=4YvVuK0V4adQJaMM2setEAx9_Ki7q6At19YfAvwyOJ8; _ga=GA1.1.1413606909.1501852025, connection=keep-alive, upgrade-insecure-requests=1, accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8, user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8, referer=http://localhost:3000/user/login, cache-control=max-age=0, accept-language=ru, method=GET, httpVersion=1.1, originalUrl=/, , responseTime=486, user=null
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +516ms
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +1ms
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +4ms
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +1ms
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +1ms
  express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +0ms
  express-session no session found +1ms
Authentication status:
    req.cookies: undefined
    req.isAuthenticated:  false
    req.session:  Session {
     cookie: 
         { path: '/',
         _expires: 2017-09-30T11:15:52.123Z,
         originalMaxAge: 2592000000,
         httpOnly: true,
         secure: false } }
     Not logged in
   express-session no session found +7ms

I see, that 'express-session' tries to fetch session with another SID O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE, that does't exist in database.

Why it happens?

AFAIK, it should fetch by SID = qiQfhyAvDDPD7muJLtGdZudKqMug0aAC

What issues can be with my setup?

One secret key, 'cookie-parser' is not used, according to recomentation 'Note Since version 1.5.0, the cookie-parser middleware no longer needs'…

I'm completely stuck with this.

Please, help.


Something is wrong with sid generation

When I manually change stored session's sid to make it match to what express-session is requesting,

UPDATE session SET sid='O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE' WHERE sid='qiQfhyAvDDPD7muJLtGdZudKqMug0aAC';

session is fetched correctly and user is persisted:

express-session fetching O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +3s
  express-session session found +2ms
> deserializeUser with id: 1
Executing (default): SELECT "id", "nickname", "email", "password", "passwordHash", "lastLoggedIn", "createdAt", "updatedAt" FROM "users" AS "user" WHERE "user"."id" = 1;
Authentication status:
    req.cookies: undefined
    req.isAuthenticated:  true
    req.session:  Session {
  cookie: 
   { path: '/',
     _expires: 2017-09-30T10:55:31.514Z,
     originalMaxAge: 2592000000,
     httpOnly: true,
     secure: false },
  passport: { user: 1 } }
    Logged in as "AntonAL"
  express-session saving O6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE +577ms
  express-session split response +1ms
info: GET / 200 585ms statusCode=200, url=/, host=localhost:3000, accept-encoding=gzip, deflate, cookie=connect.sid=s%3AO6rS2cPlQ6JDaUUxxYRAg-VI5MmldaRE.M4iFzpVZP9fNa%2FLEomsMD8D9LjA1uFnDMnXT%2FHR3wyk; meteor_login_token=4YvVuK0V4adQJaMM2setEAx9_Ki7q6At19YfAvwyOJ8; _ga=GA1.1.1413606909.1501852025, connection=keep-alive, upgrade-insecure-requests=1, accept=text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8, user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8, referer=http://localhost:3000/user/login, cache-control=max-age=0, accept-language=ru, method=GET, httpVersion=1.1, originalUrl=/, , responseTime=585, user=AntonAL

So, now it's clear – sid parameter is different, when generating session and fetching it.

like image 823
AntonAL Avatar asked Aug 31 '17 11:08

AntonAL


People also ask

What is Passportjs used for?

Passport is a popular, modular authentication middleware for Node. js applications. With it, authentication can be easily integrated into any Node- and Express-based app. The Passport library provides more than 500 authentication mechanisms, including OAuth, JWT, and simple username and password based authentication.

Where is session data stored in Express?

Session data is stored server-side. Note Since version 1.5. 0, the cookie-parser middleware no longer needs to be used for this module to work. This module now directly reads and writes cookies on req / res .

How do sessions work in Express?

How sessions works. When the client makes a login request to the server, the server will create a session and store it on the server-side. When the server responds to the client, it sends a cookie. This cookie will contain the session's unique id stored on the server, which will now be stored on the client.

Does Passport require Express session?

S o you have to use the express session mid dleware before using Passport middleware. Once you've set up the middleware, your passport strategy will come into the picture, which will be looking like this. In this example, I'm using Google Strategy.


1 Answers

Figured it out.

In client code, I was using fetch function with following code:

fetch('/user/login', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(values)
});

This approach gave me the problem in question.

When I changed request method to 'GET' in routes and used plain redirect, everything I got it to work.

window.location.href = "/login?"+querystring.stringify(values);

Why this happens?

like image 185
AntonAL Avatar answered Oct 29 '22 16:10

AntonAL