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?
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With