I am trying to use passportjs in my nodejs app with express4. For testing purpose, I am using session-file-store to keep session.
The way I set up the session and passportjs :
this.app.use(session({
secret: process.env.SESSION_SECRET || configuration.session_secret,
store: new fileStore(),
resave: false,
saveUninitialized: false
}));
// config authentication
this.app.use(passport.initialize());
this.app.use(passport.session());
// Configure Passport
passport.use('local-login', new localStrategy.Strategy(
(username, password, done) => {
Services.users.authenticatePromise(username, password).then(
function(token) {
done(null, token);
},
function(err) {
done(null, false, err);
},
).done();
},
));
// Serialize user in session
passport.serializeUser((token: AccessToken, done) => {
let user = {
_token: token.accessToken,
_expiryInAt: token.expiryInMs + new Date().getTime(),
};
done(null, user);
});
passport.deserializeUser((sessionUser, done) => {
done(null, sessionUser);
});
However, the issue I am having is that if I don't write anything into the session before user is logged in, passportjs works fine. However if I am trying to write some data into session like shopping cart details, then passportjs would not be able to serialize the user token into the session store any more. so login cannot be successful.
What am I missing?
you have a couple of things wrong with your code that I can see.
first, serializeUser should take an object and return a string (typically a unique id or database key). Your function currently takes a string and returns an object; that's backwards.
second, deserializeUser should take a string and return a user object. Your function currently takes an object and returns that same object.
finally ,serializeUser and deserializeUser must be inverses of each other; meaning, passport can call deserializeUser(serializeUser(user)) and get back the original user object.
So you're going to want something like this:
passport.serializeUser((user, done) => {
return user.id;
});
passport.deserializeUser((id, done) => {
getUserFromDatabase({id: id}, function(err, user) {
if (err) {
done(err, null); // something went wrong; return error
} else if (!user) {
done(null, false); // no user found; return `false`
} else {
done(null, user); // return user
}
});
});
for more info see here: Understanding passport serialize deserialize
Related
exports.initPassportLocalStrategy = function () {
passport.use(new LocalStrategy(
{
session: false
},
function(username, password, done) {
UserProxy.validateUserWithPassword(username, password)
.then(function (user) {
if(user) done(null, user);
else done(null, false);
})
.catch(done);
}
));
passport.serializeUser(function(user, cb) {
cb(null, user);
});
};
I'm implementing a token based auth middleware without seesion. So I was wondering why do I need to provide a serialzeUser function? I have read that the reason is to put user or some of its properties into the session and then desearlizeUser would retrieve the whole object from the session and put it in the req.user .
So here are my questions:
Why can't the done(null, user); in the LocalStrategy function put the user into req.user ? Why even bother serialise and deserailie?
If remove the searlizeUser function I will get an error, but I can get away without a deserilzeUser function, why? And in this case, who puts the user object to req.user?
Many thanks.
You don't need serialize/deserialize. Your setup is slightly wrong.
You need to move the session: false out of the strategy and into passport.authenticate. This is because strategies can't decide this, it's rather dependent on your route which kind of authentication you want.
passport.use(new LocalStrategy(
function(username, password, done) {
UserProxy.validateUserWithPassword(username, password)
.then(function (user) {
if(user) done(null, user);
else done(null, false);
})
.catch(done);
}
));
app.use(passport.initialize());
app.post('/auth', passport.authenticate(
'local', {
session: false // here goes the session false
}), doWhateverYourSetupNeeds);
Need some explanation about passport.serializeUser() and passport.deserializeUser() when not using any database for local-strategy and storing the information in simple json file.
How passport determine the id and how it retrieve back in deserializeUser()?
passport.use(
'local-signup',
new LocalStrategy(
{
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(request, email, password, done) {
process.nextTick(
function() {
if(user.doesUserExist(email)) {
return done(
null,
false,
request.flash('signupMessage', 'That email is already taken.')
);
}
else {
var newUser = user.createNewUser(email, password);
return done(null, {email: newUser.username, password: newUser.password});
}
}
);
}
)
);
// Used to serialize the user for the session
passport.serializeUser(
function serializeUserCB(user, done) {
done(null, user);
}
);
// Used to deserialize the user
passport.deserializeUser(
function deserializeUserCB(id, done) {
done(null, id);
}
);
Passport maintains aunthentication state in sessions, thus you need to define 2 functions passport.serializeUser and passport.deSerializeUser for serializing/deserializing user instances to and from the session.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session in order to support login sessions.
For serializing and deserializing it uses secret key from
app.use(session({
secret: 'ThIsIsAsEcREtKEy',
resave: true,
saveUninitialized: true
}));
Just use serializeUser and deserializeUser as
passport.serializeUser(function (user, done) {
done(null, user);
});
passport.deserializeUser(function (id, done) {
done(null, id);
});
for local Json file.
I'm surprised that it's so hard to find, or maybe something wrong with me.
I need a passport.js strategy that requires no fields - to run authentication with it on simple user GET request to '/' and manipulate user session data to, for example, make a new 'guest' user.
Maybe i can do this via passport local-strategy? Or am i need to create a custom one?
I saw this Configure Passport to accept request without body? question, yet i just simply can't figure out how the suggested in answer wrapping will change anything.
Edit: here's what I'm trying to achieve
passport.use('guest', new LocalStrategy({
passReqToCallback: true
},
function (req, NOusername, NOpassword, done) {
////NO PASSWORD OR USERNAME checks here, just deciding what kind of temporary user needs to be created///
if (req.session.passport) {
///create new user with previous session data
done(null,user)
}
else {
///create clean user
done(null, user)
})
)
and then in routes.js
app.get('/', passport.authenticate('guest'), function (req, res) {
res.render('PeerRoom.ejs', req.user);
});
Edit: Another approach could be using req.logIn and skip dealing with the strategies altogether
app.get('/', yourCustomGuestAuthenticationMiddleware, function (req, res) {
res.render('PeerRoom.ejs', req.user);
});
function yourCustomGuestAuthenticationMiddleware(req, res, next){
// No need to do anything if a user already exists.
if(req.user) return next();
// Create a new user and login with that
var user = new User({name: 'guest'+Math.random().toString() });
user.save();
req.logIn(user, next);
}
To make a new guest user, simply do it instead of rejecting an authentication
Here's a modified example from the docs
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) return done(err);
if (!user) {
/* HERE, INSTEAD OF REJECTING THE AUTHENTICATION */
// return done(null, false, { message: 'Incorrect username.' });
/* SIMPLY CREATE A NEW USER */
var user = new User({ name: 'guest'+Math.random().toString() });
user.save();
/* AND AUTHENTICATE WITH THAT */
return done(null, user);
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
I would like to use sailsJS with an existing database.
This database already contains a user table (which is named caver) and already has email, username and password (which are named contact, login, password).
I cannot change the schema of this database!
I am currently trying to use sails-generate-auth to setup a local authentication.
Is it possible to link the passport authentication with an existing model (Caver) which is not the automatically created model (User)?
Can I use a custom hash function (the one used for the password in my current database) with the passport service created by sails-generate-auth?
I already succeeded to make my authentication work with the following code but now I would like to use sails-generate-auth instead.
api/services/passport.js (without using sails-generate-auth) :
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
// helper functions
function findById(id, fn) {
Caver.findOne(id).done( function(err, user){
if (err){
return fn(null, null);
}else{
return fn(null, user);
}
});
}
function findByEmail(e, fn) {
Caver.findOne({
Contact: e
}).done(function(err, user) {
// Error handling
if (err) {
return fn(null, null);
// The User was found successfully!
}else{
return fn(null, user);
}
});
}
function md5(string) {
var crypto = require('crypto');
return crypto.createHash('md5').update(string).digest('hex');
}
function getOldGCpassword(login, password) {
return addslashes(md5(login + "*" + password));
}
function addslashes(str) {
// From: http://phpjs.org/functions
// * example 1: addslashes("kevin's birthday");
// * returns 1: 'kevin\'s birthday'
return (str + '').replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0');
}
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
passport.serializeUser(function(sessionUser, done) {
done(null, sessionUser.id);
});
passport.deserializeUser(function(id, done) {
findById(id, function (err, user) {
done(err, user);
});
});
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a email and password), and invoke a callback
// with a user object.
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(email, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by email. If there is no user with the given
// email, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByEmail(email, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: 'Unknown user ' + email });
}
var hash = getOldGCpassword(user.Login, password);
if (hash.localeCompare(user.Password) == 0) {
var returnUser = { email: user.Contact, createdAt: user.Date_inscription, id: user.Id };
return done(null, returnUser, { message: 'Logged In Successfully'} );
} else {
return done(null, false, { message: 'Invalid Password'});
}
})
});
}
));
I'm using Passport for authentication in conjunction with node-orm2. Passport requires you to register functions for serializing and deserializing users to and from the session. My users are stored in a database which I'm accessing through node-orm2's Express middleware. The orm2 middleware tags its models onto the req object for easy access. Unfortunately, Passport does not provide access to the req object in deserializeUser. I've come across this solution, but am hoping for something better:
var User;
passport.use({ passReqToCallback: true }, new LocalStrategy(function (request, username, password, done) {
if (!User) {
User = request.models.User;
}
User
.find({ username: username })
.limit(1)
.run(function (err, users) {
var user = users[0];
if (err) {
done(err);
} else if (!hasher.verify(password, user.password)) {
done(null, false);
} else {
done(null, user);
}
});
}));
passport.deserializeUser(function (id, done) {
User.get(id, done);
});
req will be passed to passport's serialize and deserialize callbacks in the upcoming release, 0.2.0: https://github.com/jaredhanson/passport/pull/160