Passport: session serialization failed with serialization functions present - serialization

I'm adding authentication to node app using `feathers-passport with the following configuration:
.configure(feathersPassport({
secret: 'some-secret',
store: new MongoStore({db: 'demo-session'}),
resave: false,
saveUninitialized: false
}))
And I configure passport:
var localOptions = {
usernameField: 'email',
passwordField: 'password'
};
passport.serializeUser(function(user, done) {
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
api.service('users').get(id, {}, done);
});
passport
.use(new LocalStrategy(localOptions, function(email, password, done){
api.service('users').authenticateLocal(email, password, done);
}))
To authenticate, I have this route setup:
.post('/users/authenticate', function(request, response, next){
passport.authenticate('local', function(error, user){
//error comes back as null and user is a valid object
request.login(user, function(error){
if(error) return next(error);
if(!user) return next(new Error("no user found"));
else {
var token = tokenIssue({id: user.id});
response.status(201).json({token: token, id: user.id});
}
});
})(request, response, next);
})
However, the session serialization always fails with the standard error: Failed to serialize user into session, which would normally indicate that the serializeUser and deserializeUser functions aren't present. Except in this case they are. I went through the passport source and here (line 265 of authenticator.js):
var stack = this._serializers;
(function pass(i, err, obj) {
// serializers use 'pass' as an error to skip processing
if ('pass' === err) {
err = undefined;
}
// an error or serialized object was obtained, done
if (err || obj || obj === 0) { return done(err, obj); }
var layer = stack[i];
if (!layer) {
return done(new Error('Failed to serialize user into session'));
}
this._serializers prints out [] so when var layer = stack[i] is called, the check if(!layer) always passes and thus the error is thrown. It would seem like passport isn't picking up that I've defined the serialization functions and I'm unsure if that's a result of passport or a consequence of using feathers-passport. I followed the feathers-passport guide very closely and I can't figure out what exactly is going wrong. Any help would be appreciated.

Related

Error [ERR_HTTP_HEADERS_SENT] : Cannot set headers after they are sent to the client

app.post("/login", async (req, res) => {
// Destructure Req Body
const { email, password } = req.body;
// Validate Body
if (!email || !password) {
res.status(400).json({ success: false, message: "PARAMS_MISSING" });
}
// Build the SQL query
const query = `SELECT * FROM user WHERE email = "${email}"`;
// Get the user from DB
const user = await db(query);
// Check if password is valid
const isPasswordValid = decryptPassword(user.hash_password, password);
// Return if password is not valid
if (!isPasswordValid) {
res.status(401).json({ success: false, message: "INAVLID_PASSWORD" });
}
// Generate Token
const token = generateToken({ id: user.id, email: user.email });
// Save Cookie
res.cookie("token", token, { maxAge: 900000, httpOnly: true });
res.end();
// Return
res.json({ success: true, message: "USER_AUTHENTICATED" });
});
UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I m getting this error again n again idk what to do i m beginner,
I'm facing this weird issue in NodeJS when using with Passport.js, Express. Basically, I get an error saying "Cannot set headers after they are sent to the client" even though I don't send more than one header.
This error means the 'res' object respond twice. E.g: in your '// Validate body' if the password or email are missing your http connection respond 'res.status().json().'(note that is closing the http connection), but as you didn't stop the execution of the code, it carries on then it may respond a second time in the // Return if password is not valid which create the err as the header can not be set twice and the connection is already close.
Than more here you error is Unhandled, as an Async function reject, the error must be handle, wrapping the code in a try{} catch(e){} will fix it.
So that should fix your issues
app.post("/login", async (req, res) => {
try{
// Destructure Req Body
const { email, password } = req.body;
// Validate Body
if (!email || !password) {
res.status(400).json({ success: false, message: "PARAMS_MISSING" });
return // stop execution of the function
}
// Build the SQL query
const query = `SELECT * FROM user WHERE email = "${email}"`;
// Get the user from DB
const user = await db(query);
// Check if password is valid
const isPasswordValid = decryptPassword(user.hash_password, password);
// Return if password is not valid
if (!isPasswordValid) {
res.status(401).json({ success: false, message: "INAVLID_PASSWORD" });
return // stop exec of the function
}
// Generate Token
const token = generateToken({ id: user.id, email: user.email });
// Save Cookie
res.cookie("token", token, { maxAge: 900000, httpOnly: true });
res.end();
// Return
res.json({ success: true, message: "USER_AUTHENTICATED" });
} catch(err) {
console.error(err) // code to handle the err
}
});
But still, a problem remain as at the end of your script, you have a res.end()(which terminate the connection) and right after a res.json() which will fail as the connection has been close the line before (than more the statusCode is missing)

Passport local strategy is never get called

I know this question was asked many times in stack over flow. I tried every accepted answers but can't my local strategy into function. Here is my code
var express = require('express');
var app = express();
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.json({limit: '50mb'}));
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
app.set('trust proxy', 1); // trust first proxy
app.use(session({
secret: '564sdf4as564f56a7s765s4afjkgadxjkbadksj',
resave: true,
saveUninitialized: true,
cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy({
usernameField:'userName',
passwordField:'password',
passReqToCallback : true
},function(request, userName, password, done) {
console.log(request);
UserAccount.findOne({'userName': userName} , function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, 'Incorrect username.' );
user.verifyPassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, 'Incorrect password.');
}
});
});
}));
passport.serializeUser(function(user, done) {
console.log('Serialize user called');
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log('Deserialize user called');
UserAccount.findById(id, function(err, user) {
done(err, user);
});
});
Then I created a router like
var router = express.Router();
require('./controllers/user')(router,passport);
app.use('/api',router);
Then in my user controller I created signIn function like
app.post('/signIn',function (request,response,next){
var variables = request.body;
console.log(variables);
passport.authenticate('local', function(error, user, info) {
console.log(user);
if (error) { console.log(error); return next(err); }
if (!user) { return response.redirect('/login'); }
response.logIn(user, function(err) {
if (err) { return next(err); }
return response.redirect('/users/' + user.username);
});
})(request, response, next);
});
Then I send a request from "Postman"
{
"userName":"karthik#abc.com",
"password":"qwerty"
}
My mongodb userName and password fields are same.
In my db there is an account with this user name and password. But every time it return 'user' as 'false' inside authenticate. I tried to console my request inside local strategy but it never gets called. I don't understand What I done wrong here? Can some one help to solve this? Thank you very much.
You should name local strategy and use it in authenticate.
Use like this passport.use('local-strategy',new LocalStrategy({});
and like passport.authenticate('local-strategy');

passport.js deserializeUser not modifying req.user

I am not seeing any effect on the req.user or req.session.passport.user objects as a result of deserializeUser().
My understanding is that successful completion of deserializeUser() should result in my User object which I retrieve from a DB should be set to the req.user property. Is this correct?
Furthermore, it seems that the purpose of this callback is to allow me to serialize a small object (e.g.: {username: 'me', email: 'me#me.com'}) in the cookie itself, but then add more user info retrieved from the database to use downstream.
Instead, I see the following:
passport.serializeUser(function(user, done) {
// user: 'stan#stadelman.com'
fetchUser(user, function(u) {
// u: { email: 'stan#stadelman.com', name: 'Stan Stadelman' }
done(null, u);
});
});
passport.deserializeUser(function(id, done) {
// id: { email: 'stan#stadelman.com', name: 'Stan Stadelman' }
fetchUser(id.email, function(user) {
// user: { email: 'stan#stadelman.com', name: 'Stan Stadelman', local_props: 'from_db'}
done(null, user);
});
});
req.user = 'stan#stadelman.com'
req.session = { cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: { user: { email: 'stan#stadelman.com', name: 'Stan Stadelman' } } }
My expectation was that the req.session.passport.user and/or req.user objects should contain the local_props property value. Am I reading the documentation & tutorials incorrectly, or is there an issue? I've cross-posted in passport.js github here.
My express & passport setup is as follows:
// express configs
app.use(cookieParser(expressSecret));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(expressSession({
secret: expressSecret,
resave: true,
saveUninitialized: true }
));
app.use(passport.initialize());
app.use(passport.session());
// passport implementation
passport.use(new BasicStrategy(/...));
You should pass user id to done callback in serialize
passport.serializeUser(function(user, done) {
done(null, user.id);
});
And then use that id to fetch user in deserialize
passport.deserializeUser(function(id, done) {
fetchUser(id, function(user) {
done(null, user);
});
});
I run into a similar problem but caused by something else. Passport.js deserializeUser() does its job setting user object to req.user:
passport.deserializeUser(async function(id, done) {
try {
const account = await Account.findById(id);
done(null, account);
} catch (error) {
done(error, null)
}
});
However, in order to check whether a user is logged in, I have a route on the server /account/me that takes req.user and returns it as json:
router.route('/account/me').get(mwAuthentication, (req, res) => {
res.json(req.user);
});
To filter sensitive properties, I have a method toJSON() defined in Mongoose schema:
AccountSchema.methods.toJSON = function() {
let account = this;
let accountObject = account.toObject();
return _.pick(accountObject, ['_id', 'fullName', 'email', 'facebookId', 'photoUrl']);
};
When you call res.json(req.user) it calls JSON.stringify(req.user) internally, which again calls my toJSON() method and limits the object to properties that I picked.
Hope this helps someone.

req (request) is not defined for passportjs.use in SailsJS

I want to use req.flash("message" : error ) to support error message callbacks for passportJS inside of SailsJS. However, PassportJS does not handle the below during callbacks: (similar post: PassportJS Custom Authenticate Callback Not Called)
//there is no req found
req.flash("message" , "invalid password");
This usually will be alright if there's something like:
function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
//....
req.flash("message" , "invalid password");
}
But I can't use it inside passport.use.
/services/passport.js
passport.use(new HttpBasicStrategy(
function(username, password, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure. Otherwise, return the authenticated `user`.
findByUsername(username, function(err, user) {
if (err)
return done(null, err);
if (!user) {
return done(null, false, {
message: 'Unknown user ' + username
});
}
bcrypt.compare(password, user.password, function (err, res) {
if (!res){
--> //there is no req found
--> req.flash("message" , "invalid password");
return done(null, false, {
message: 'Invalid Password'
});
}
var returnUser = {
username: user.username,
createdAt: user.createdAt,
id: user.id
};
return done(null, returnUser, {
message: 'Logged In Successfully'
});
});
})
});
}
));
Is there another way to call req.flash? I'm pretty new at express and sailsjs, please pardon my ignorance.
For sails v0.10.x sails-generate-auth is perfect for this.

Sessions with express.js + passport.js

Goal
What I want to do:
Create a session for the user
Create a session for the socket (socket.io)
Use passport.js to authenticate the login and socket sessions.
Notes
I have installed MongoStore and passport.socket.io npm's. I can login and set the cookie of the user logged in (connect.sid)
QUESTION
How do I setup the system to store socket sessions and couple them with the session of the user?
Code
app.js
/* The usual express setup */
passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
User = require('./models/user.js'),
MongoStore = require('connect-mongo')(express);
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({
secret: 'chuck norris',
store: new MongoStore({db: User.name}, // the db's name
function(err) {
console.log(err || 'connect ok!');
})
}));
app.use(express.methodOverride());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.js (the passport part)
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
User.findOne({username: username}, function(err, user) {
if(!user) {
return done(null, false, {message: 'Incorrect Username!'});
}
if(!user.validPassword(password)) {
return done(null, false, {message: 'Incorrect Password!'});
}
return done(null, user);
});
}
));
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('/',
passport.authenticate('local'),
function(req, res) {
res.redirect('/home/'+req.user.username);
});
app.js (socket.io part)
io.set('authorization', passportSocket.authorize({
key: 'connect.sid',
secret: 'chuck norris',
store: /* Not entirely sure what goes here */
fail : function(data, accept) { accept(null, false); },
success: function(data, accept) { accept(null, true); }
}));
io.sockets.on('connection', function(socket) {
console.log('User Connected: ' + socket.handshake.user.username);
});
You store your new memory story object instance into a variable and pass it in to both express and socket io like so.
(be aware that we are using different stores but in theory it should not matter what store you use as long as you pass off control the proper way)...
var ...
,MemoryStore = express.session.MemoryStore
,sessionStore = new MemoryStore();
then in app.configure you...
app.use(express.session({store:sessionStore,secret:'secret',key:'express.sid'}));
and finally in socket.io configure
io.configure(function (){
io.set("authorization", passportSocketIo.authorize({
key: 'express.sid', //the cookie where express (or connect) stores its session id.
secret: 'secret', //the session secret to parse the cookie
store: sessionStore, //the session store that express uses
fail: function(data, accept) {
// console.log("failed");
// console.log(data);// *optional* callbacks on success or fail
accept(null, false); // second param takes boolean on whether or not to allow handshake
},
success: function(data, accept) {
// console.log("success socket.io auth");
// console.log(data);
accept(null, true);
}
}));
If you have done this correctly and your user successfully authenticates you should then be able to access the session data on the handshake object.
console.log(socket.handshake.user.username);
//or sometimes it might be...
console.log(socket.handshake.user[0].username);
Hope that helps.