Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get token on the client-side for passport-local authentication

EDIT I tried both, redirecting and not redirecting. When redirecting I get the same result as if I had directly requested /, when I don't what I receive is an object containing data = Moved Temporarily. Redirecting to /

Given this strategy :

var strategy = new LocalStrategy(
{
  session: true
},
function (username, password, done) {
  User.findOne({ username: username }, function(err, user) {
    if (err) { return done(err); }
    if (!user) {
      return done(null, false, { message: 'Incorrect username.' });
    }
    if (user.password !== password) {
      return done(null, false, { message: 'Incorrect password.' });
    }
    return done(null, user);
  });
});

And this server :

//************ EDIT ****//
function genuuid(req) {
  return "Test UID";
}
//************ EDIT ****//

var app = express();
var router = express.Router();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(passport.initialize());

//************ EDIT ****//
app.use(session({
  genid: function(req) {
    return genuuid() // use UUIDs for session IDs
  },
  secret: "somethingSecret",
  cookie: {maxAge: 60000 },
  resave: true,
  saveUninitialized: false }));
//************ EDIT ****//

passport.use(strategy);

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }));

server = http.createServer(app).listen(port, function(){
  console.log('Express server listening on port ' + app.get('port'));
})

I'm trying to get the generated token from my client (a mocha test) like this:

it('Login should redirect to home and return a user token', function(done) {
  var postData = querystring.stringify({
    username : user,
    password : password
  });

  var options = {
    uri: 'http://' + hostname + ':' + port +  '/login',
    followRedirect: false
  };

  request.post(options)
    .form(postData)
    .on('response', function(res) {
      res.statusCode.should.equal(302);
    })
    .on('data',function(data) {
      should.exist(data);
      should.fail(0,1,'Test not implemented');
      done();
    })
    .on('error', function(e) {
      should.fail(0,1,'Problem with request: ' + e.message);
  });
});

Headers contain

{ 'x-powered-by': 'Express',
  location: '/',
  vary: 'Accept',
  'content-type': 'text/plain; charset=utf-8',
  'content-length': '35',
  date: 'Tue, 08 Sep 2015 19:46:03 GMT',
  connection: 'keep-alive' }

There are no cookies or whatever. Where is my token?

like image 490
Luis Sieira Avatar asked Sep 06 '15 14:09

Luis Sieira


People also ask

What is local in Passport authenticate?

The local authentication strategy authenticates users using a username and password. The strategy requires a verify callback, which accepts these credentials and calls done providing a user.

Does Passport-local use JWT?

A Passport strategy for authenticating with a JSON Web Token. This module lets you authenticate endpoints using a JSON web token. It is intended to be used to secure RESTful endpoints without sessions.

What is Passport token?

This module lets you authenticate using a token in your Node. js applications. It is based on passport-local module by Jared Hanson. By plugging into Passport, token authentication can be easily and unobtrusively integrated into any application or framework that supports Connect-style middleware, including Express.


1 Answers

Try using a session middleware for sessions, such as express-session:

var session = require('express-session');
// Code to set up your app
var app = express();

// Make sure to replace "somethingSecret" with your own secret value
app.use(session({secret: "somethingSecret", resave: true, saveUnitialized: false}));

I wrote up an app very close to the one you provided, and saw the same issue, which was strange for me because I've used express and passport many times before. When I realized I wasn't using any session middleware, I added the express-session middleware and voila; set-cookie header.

Note, however, that this code is not actually ready for production yet. From the express-session repository:

Warning The default server-side session storage, MemoryStore, is purposely not designed for a production environment. It will leak memory under most conditions, does not scale past a single process, and is meant for debugging and developing.

For a list of stores, see compatible session stores.

[EDIT]

Using this code (User is stubbed to always return the same values):

var bodyParser = require('body-parser');
var express = require('express');
var http = require('http');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var session = require('express-session');

// Stub of User schema for testing
var User = {
    findOne: function(user, callback) {
        callback(null, {
            id: 1234,
            username: user.username,
            password: 'foo'
        });
    },
    findById: function(id, callback) {
        callback(null, {
            id: 1234,
            username: 'user',
            password: 'foo'
        });
    }
};

var strategy = new LocalStrategy(
{
  session: true
},
function (username, password, done) {
  User.findOne({ username: username }, function(err, user) {
    if (err) { return done(err); }
    if (!user) {
      return done(null, false, { message: 'Incorrect username.' });
    }
    if (user.password !== password) {
      return done(null, false, { message: 'Incorrect password.' });
    }
    return done(null, user);
  });
});

function genuuid(req) {
  return "Test UID";
}

var app = express();
var router = express.Router();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(passport.initialize());

app.use(session({
  genid: function(req) {
    return genuuid() // use UUIDs for session IDs
  },
  secret: "somethingSecret",
  cookie: {maxAge: 60000 },
  resave: true,
  saveUninitialized: false }));
  
passport.use(strategy);

passport.serializeUser(function(user, done) {
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});
  
app.post('/login', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }));

server = http.createServer(app).listen(51015, function(){
  console.log('Express server listening on port ' + app.get('port'));
});

Running the test:

var should = require('should');
var assert = require('assert');
var request = require('request');
var querystring = require('querystring');

it('Login should redirect to home and return a user token', function(done) {
  var postData = querystring.stringify({
    username : 'user',
    password : 'foo'
  });

  var options = {
    uri: 'http://' + '127.0.0.1' + ':' + '51015' +  '/login',
    followRedirect: false
  };

  request.post(options)
    .form(postData)
    .on('response', function(res) {
      console.log(res.headers);
      res.statusCode.should.equal(302);
    })
    .on('data',function(data) {
      should.exist(data);
      should.fail(0,1,'Test not implemented');
      done();
    })
    .on('error', function(e) {
      should.fail(0,1,'Problem with request: ' + e.message);
  });
});

Gives the following output; notice the set-cookie header is included:

mocha


{ 'x-powered-by': 'Express',
  location: '/',
  vary: 'Accept',
  'content-type': 'text/plain; charset=utf-8',
  'content-length': '35',
  'set-cookie': [ 'connect.sid=s%3ATest%20UID.NC0hY3rYGMdnN560ewpf%2FaJbO%2BNd9M7QLz3gnse0eLs; Path=/; Expires=Wed, 09 Sep 2015 13:08:06 GMT; HttpOnly' ],
  date: 'Wed, 09 Sep 2015 13:07:06 GMT',
  connection: 'close' }
  1) Login should redirect to home and return a user token

  0 passing (69ms)
  1 failing

  1)  Login should redirect to home and return a user token:

      Uncaught AssertionError: Test not implemented

Except for the stubbed out User schema and the hardcoded config values, my code should be the same as yours, so I'm sort of at a loss as to how you aren't getting the set-cookie header.

Are you using any other middleware for express that might affect the session or cookies?

[EDIT 2]

The problem is that (in your repository) you are adding the session middleware in a different scope than the one in which server.listen executes, so the changes made to app don't actually take affect.

Instead of having the server start listening before the group of tests, you should have it start once for each test, and pass all of the parameters you need into it immediately. I've submitted a pull request to your repository with the details of how I might do this, as it's not a small amount of code and wouldn't really be ideal to paste in here.

Please take the time to read and understand the changes, because I have only been concerned with making the session token be sent, and I make no guarantee as to whether other functionality is affected by these changes.

like image 97
Caleb Brinkman Avatar answered Oct 05 '22 08:10

Caleb Brinkman