Passport.js: Serialization issue with local login strategy - node.js

I'm having trouble authenticating a small passport.js example project when I switch over to mongodb instead of a local array.
MongoDB is able to connect and add users from a form on the home page. On login however, there are problems.
Not sure if this matters but on the /login page onload I see 2 red errors in the browser console:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
login:1
Unchecked runtime.lastError: The message port closed before a response was received.
I put a log statement in the relevant route handler. It does not reach or log to the console.
app.post('/login', function sendUserRecordToAxiosOrRedirect(req, res, next) {
passport.authenticate('local', function controlErrorsOrSuccess(error, userRecord, info) {
console.log('inside pp.auth callback');
if (error) return next(error);
if (userRecord == false) return res.json ({ "error" : "Invalid Username" });
if (userRecord) {
console.log('---- User Found: INFO --------');
console.log(info);
res.redirect('/')
}
})
});
From the above I suspected something is awry with the Strategy I give to passport. The log statement in this one doesn't fire either:
passport.use(new Strategy(
function(username, password, done) {
console.log('inside Strategy');
db.users.findInDatabaseByUsername(username, function (error, userRecord) {
if (error) return done(error);
if (userRecord == false) return done(null, false); // no error, no userRecord
if (userRecord.password !== password) return done(null, false);
return done(null, userRecord);
})
}
));
Since I altered my serialization functions to search the database instead of an array I suspect the problem may lie here. Neither of the log statements here fire either:
const passUserIdOnlyToACallback = function(userRecord, callbackFunction) {
console.log("serializing...");
callbackFunction(null, userRecord.id);
};
passport.serializeUser(passUserIdOnlyToACallback);
function passUserRecordToCallbackById(userId, verifyCallback) {
db.users.findInDatabaseById(userId, function(error, userRecord) {
console.log("deserializing...");
if (error) return verifyCallback(error);
verifyCallback(null, userRecord)
})
}
passport.deserializeUser(passUserRecordToCallbackById);
Everything was working fine initially with the local array code, so my alterations broke something. Does anyone see the error?

Related

router.get inside router.post, can't reach router.get

am trying to have a router.get inside router.post, I need to compare the information provided by the user to the one in the database and then post it if it does not exists. The problem is the router.get is never reached. No errors, and postman keeps "sending request" with no end. Is it possible to have a router.get inside router.post?, if yes how?, If no, how do I get info from router.get to pass to router.post? I need to run the api from ..../new, and do all the work from there. Thanks in advance
//register
router.post('/new', (req, res, next)=>{
console.log("jumped out");
var user_id, password0, password1;
user_id = req.body.user_id;
password0 = req.body.password0;
password1 = req.body.password1;
console.log(password1);
//retrieving usernames to check if it exists
router.get('/accounts', (req, res, next)=>{
console.log("in here");
detail.find(function(err, accounts){
//looping through usernames
for (var i=0; i<accounts.length; i++){
if (accounts[i].user_id === user_id){
res.json({msg: 'Username taken'});
}
else if(i == (accounts.length-1)){
if (password0 === password1){
let newAccount = new account({
user_id: this.user_id,
password: this.password0
});
newAccount.save((err, account)=>{
if(err){
res.json({msg: 'failed to create account'});
}
else{
res.json({msg: 'Account created successfully'});
}
});
}
else if (password0 !== password1){
res.json({msg: 'Password mismatch'});
}
}
}
//res.json(accounts);
});
});
});
I think the issue here is that you have a misunderstanding about what router.get is doing. It sounds like you want to accept a POST request from the user, then make a get request to check if the data already exists, and if not, then update your database.
If this is the case, the inner GET should actually use something like axios to make a request. You can make an axios request like this:
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
If you are actually trying to accept a second request from the user after the POST, then you should NOT do that. Users should only make one request for one action. You'll run into all kinds of issues down that path.
Turns out you can still retrieve data with only .POST, without having .GET. Removing the line having router.get, fixed the problem.
router.get('/accounts', (req, res, next)=>{

Can't set headers after they are sent when using dbquery multiple times

I get this error #Can't set headers after they are sent# when using dbquery multiple times.
Hi, I'm almost new to node.js and I can't figure out why this error appears. If I try with one dbquery then everything is ok. But if I use multiple query then it crashes.
router.get(
'/auth/facebook/token',
(req, res, next) => {
passport.authenticate('facebook-token', (error, user, info) => {
if (user){
myDB.setUser(user.email,(err,pinfo) =>{
if (err) {
res.send(err);
}
else {
res.send(pinfo); <- Crash at this line!!!
}
});
}
next();
})(req,res, next);
}
);
function setUser (email, cb) {
pool.query("INSERT INTO users (email,pinfo) VALUES (?,0)", email, (err, result) => {
if(err){
cb(err);
return;
}
else{
pool.query("SELECT pinfo FROM users WHERE email = ?", email,(err,pinfo) =>{
cb(err,pinfo);
});
}
});
}
You are calling next middle layer function using next(). After sending the response to the user. Try without next or modify your logic to. Hope this will help you
once you are used res.send you cannot use it again
it seems you are sending a response at two positions please check the logic thoroughly
Some Where you sending response twice and hence this error. R.sarkar is right take out next() call and add it somewhere you will want to continue with your next dbquery or else respond from top function only once and never call next().

A promise was created in a route but not returned from

i tried to find a solution for this warning i get, but none of the suggested approaches worked for me. I don't want to disable this warning, because i believe this warning is there for a reason, but i can't figure out whats the real deal here.
I have the following route defined on my node.js server running express:
// GET all items
router.get('', helpers.loginRequired, function(req, res) {
debug("Get all items");
return knex.select('*').from('routes')
.then((routes) => {
res.send(routes);
return null;
})
.catch((err) => {
debug("error getting routes");
res.send({ error: "Fehler beim Laden" });
return null;
});
});
My Middleware loginRequired does not use any promise at all
function loginRequired(req, res, next) {
if (!req.user) {
req.flash('loginMessage', 'Du musst eingeloggt sein!');
req.session.redirectTo = req.path;
return res.redirect('/');
} else {
return next();
}
}
I use knex for accessing my database, which creates promises.
And everytime i hit the route, i get the following warning:
(node:10568) Warning: a promise was created in a handler but was not returned fr
om it, see *link*
at Function.Promise.attempt.Promise.try (C:\Users\qxr1088\Desktop\Privat\GPS
\bibis-tracker\node_modules\bluebird\js\release\method.js:29:9)
As you can see i tried to add the "return null;" statements as suggested by bluebird, but this does not fix the problem.

passportjs custom callback 200

I am using angularjs and jade templating for my client side. And node and express on the server side. I recently added passport authentication and have the local strategy working fine when the login is attempted with valid credentials. It's the error cases that I'm having trouble with. I have implemented the custom callback to get my error messages to bubble back to the client. Now I get the error messages but the status gets set to 200 somewhere, instead of the 401 I expect.
// login route
app.post("/api/login", function(req, res, next) {
passport.authenticate("local", function(err, user, info) {
if (err) {
return next(err);
}
if (user === false) {
res.status = 401;
res.send(info.message);
} else {
res.json({success:"User logged in"});
}
})(req, res, next);
});
and this is the angular controller that submits the login request:
var loginObj = {email: $scope.login.email,
password:$scope.login.password};
$http.post("/api/login", loginObj)
.success(function(data, status) {
console.log("successful login");
console.log("data = " + JSON.stringify(data));
console.log("status = " + status);
})
.error(function(error) {
console.log("error logging in.");
console.log("error = " + JSON.stringify(error));
$scope.error = error;
});
So, suppose I have a password mismatch... the error in my validation routine sets my error message to "Invalid password, please try again." But what I see on the console is:
successful login
data = "Invalid password, please try again."
status = 200
I'm not resetting the status anywhere else. How is it getting set to 200? What am I doing wrong? Sorry, I'm a relative newbie to node and angular, so any help would really be appreciated. I know I'm not understanding some key point, I just can't figure out what. Thanks!
It's res.status(401), not res.status = 401.
res.status() is a function.

Handle jade errors in res.render()

I have a working node.js / express based server and am using jade for templating. Usually there is no problem but a couple of times every day I get an error message when requsting any page. The error is 'failed to locate view'. I don't know why i get this error since it worked fine just minutes before.
The question however is how I can force a crash on this event, for example:
res.render('index.jade', {info: 'msg'}, function(error, ok) {
if (error)
throw new Error('');
// Proceed with response
};
How would I do this? And how would I proceed with the response?
thank you.
You can add an error handling middleware.
app.use(function handleJadeErrors(err, req, res, next) {
// identify the errors you care about
if (err.message === 'failed to locate view') {
// do something sensible, such as logging and then crashing
// or returning something more useful to the client
} else {
// just pass it on to other error middleware
next(err);
}
});
Try this:
app.use(function (req, res, next) {
fs.exists(__dirname + '/views/' + req.url.substring(1) + '.jade', function (exists) {
if(!exists) {
console.log(err);
return next();
}
res.render(req.url.substring(1), { title: "No Controller", user: req.session.user });
}
});

Resources