Express JS res.redirect is undefined - javascript

I follow this tutorial on how to use passport-facebook in MEAN stack, I got this error when I try to use res.redirect('/signup') I always get this error: ReferenceError: res is not defined
it seems like I didn't pass res as a parameter in my function. I try to add it at second / last parameters. but nothing works. What do I miss???
Here is my code:
exports.saveOAuthUserProfile = function(req, profile, done){
User.findOne({
provider: profile.provider,
providerId: profile.providerId,
},
function(err, user) {
if (err) {
return done(err);
}
else
{
if (!user) {
// console.log(profile.email.value);
var possibleUsername = profile.username || ((profile.email) ? profile.email.value.split('#')[0] : '');
User.findUniqueUsername(possibleUsername, null, function(availableUsername){
profile.username = availableUsername;
user = new User(profile);
user.save(function(err){
if (err) {
var message = getErrorMessage(err);
// var message = _this.getErrorMessage(err);
req.flash('error', message);
return res.redirect('/register');
}
return done(err, user);
});
});
}
else
{
return done(err, user);
}
}
});
};

Related

Unable to access user info object property - facebook chat api

I'm using Facebook chat api to create a simple cli script that will reply to messages that are sent to my facebook account. I'm trying to assign and get the user name and my name to use them inside the reply but they are always undefined. I think that the object property aren't assigned correctly. Is there a fix for this?
require('dotenv').config();
const fs = require('fs');
const fb = require('facebook-chat-api');
const path = require('path');
const appStateFile = path.format({ dir: __dirname, base: 'appstate.json' });
let currentUser = null;
if( !fs.existsSync(appStateFile) ){
//debug .env
console.log(process.env);
fb({email: process.env.FB_EMAIL, password: process.env.FB_PWD}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
fs.writeFileSync(appStateFile, JSON.stringify(api.getAppState()));
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile); // profile is logged correctly
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user); // user object is logged correctly
api.sendMessage('...', event.threadID)
});
}
});
});
}else{
fb({appState: JSON.parse(fs.readFileSync(appStateFile))}, (err, api) => {
if(err){
return console.log(err);
}
console.log(api);
api.setOptions({
listenEvents: true
});
let id = api.getCurrentUserID();
api.getUserInfo(id, (err, profile) => {
console.log(profile);
currentUser = profile;
});
api.listenMqtt( (err, event) => {
if(err){
return console.log(err);
}
if(event.type === 'message'){
console.log(event.body)
api.getUserInfo(event.senderID, (err, user) => {
if(err){
return console.log(err);
}
console.log(user)
api.sendMessage(`FB Pager v1.0.\nHi ${user.name}!Your message was forwarded with an email to ${currentUser.name}.`, event.threadID)
});
}
});
});
}
I think the problem here is that api.getUserInfo is asynchronous.
So you would need to nest them to get it to work.
Or you can try this, since getUSerInfo allows you to add an array of user ids to get the data for:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
const currentUserId = api.getCurrentUserID();
const senderId = event.senderID;
api.getUserInfo([currentUserId, senderId], (err, ret) => {
if(err) return console.error(err);
// Ret should contain the two users
// See: https://github.com/Schmavery/facebook-chat-api/blob/master/DOCS.md#getUserInfo
console.log(ret);
});
}
});
Nesting user calls method:
api.listenMqtt((err, event) => {
if (err) {
return console.log(err);
}
if (event.type === "message") {
let currentUserId = api.getCurrentUserID();
api.getUserInfo(currentUserId, (err1, signedInUser) => {
if (err1) {
return console.log(err);
}
api.getUserInfo(event.senderID, (err2, userInMessage) => {
if (err2) {
return console.log(err);
}
console.log(signedInUser, userInMessage)
api.sendMessage("...", event.threadID);
});
});
}
});
After a lot of debug I've found the correct way to access the needed informations. Since the user informations after that are retrived are mapped to another object that is the userId, the only way to access to each property is to use a for loop. Initially I was thinking that this can be avoided but unfortunately it's necessary otherwise using only dot notation will result in undefined. This is how I've solved
api.getUserInfo(userId, (err, user) => {
let username;
if(err){
return console.log(err);
}
for(var prop in user){
username = user[prop].name;
}
api.sendMessage(`Hello ${username!}`, event.threadID);
});

MongoDB returning an onject that doesnt exist

I was working on a project with MongoDB and passport, when i ran into this error, event though p1 isn't used, it still reruns an object im guessing, because it just says that the field p1 is taken, when it isn't. The same is happening with p2. Does anyone know why ?
passport.use(
"local.signup",
new LocalStrtegy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true,
},
async function (req, email, password, done) {
req.checkBody("email", "E-mail is empty").notEmpty();
req
.checkBody("password", "Your password is too short!")
.isLength({ min: 4 });
var errors = await req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash("error", messages));
}
const p1 = User.find({ p1: req.body.p1 });
const p2 = User.find({ p2: req.body.p2 });
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, {
message:
"This E-Mail alredy in use! If you believe that this is an error, please an admin on. (ERR 002 MCEE)",
});
} else if (p1) {
return done(null, false, {
message:
"This username is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCEM)",
});
} else if (p2) {
return done(null, false, {
message:
"This Tag is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCED)",
});
}
console.log(mc + " " + dcign + " " + user);
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(req.body.password);
newUser.p1 = req.body.p1;
newUser.p2 = req.body.p2;
newUser.Banned = false;
console.log(req.body);
newUser.save(function (err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}
)
);
Calling User.find returns a Promise which you are not awaiting. So when you are checking for existence of p1 and p2, it returns a truthy value as both values are Promise objects.
To fix the issue use await in front of both User.find like this
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
After that both values will be array as you are using find method so just check for length property or better use findOne instead of find method.
const p1 = await User.findOne({ p1: req.body.p1 });
const p2 = await User.findOne({ p2: req.body.p2 });
MongoDB .find returns an array. In your case p1 is an empty array. if(p1) will always return true. You should check for its length.
You should use await for your query calls.
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
I'm pasting sample code below -
passport.use(
"local.signup",
new LocalStrtegy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true,
},
async function (req, email, password, done) {
req.checkBody("email", "E-mail is empty").notEmpty();
req
.checkBody("password", "Your password is too short!")
.isLength({ min: 4 });
var errors = await req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash("error", messages));
}
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, {
message:
"This E-Mail alredy in use! If you believe that this is an error, please an admin on. (ERR 002 MCEE)",
});
} else if (p1.length) { // Check for Length
return done(null, false, {
message:
"This username is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCEM)",
});
} else if (p2.length) { // Check for Length
return done(null, false, {
message:
"This Tag is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCED)",
});
}
console.log(mc + " " + dcign + " " + user);
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(req.body.password);
newUser.p1 = req.body.p1;
newUser.p2 = req.body.p2;
newUser.Banned = false;
console.log(req.body);
newUser.save(function (err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}
)
);

Can I pass my own data (object) back from passport.use('local') to passport.authenticate?

I want to pass my own data from passport.use to passport.authenticate.
I thought that the info parameter in
passport.authenticate('local', function(err, user, info)
could be used for that.
So is there a way to doing this ?
My auth route
passport.authenticate('local-register', (err, user, info) => {
if (err) {
return next(err); // 500 status
}
console.log(info);
if (info) {
console.log('rendering info ' + info);
return res.render('auth/register', { info });
} else {
if (!user) {
return res.status(409).render('auth/register');
}
req.login(user, err => {
if (err) {
console.log(err);
return next(err);
}
return res.redirect('auth/profile');
});
}
})(req, res, next);
});
My config file
module.exports = passport => {
passport.use(
'local-register',
new LocalStrategy(
{
...
},
(req, email, password, done) => {
// Check if form is filled out correctly
let errors = [];
//check for same email
SCUser.findOne({ 'local.email': req.body.email }, (err, local) => {
if (local) errors.push({ text: 'Email already in use.' });
//check for same passwords
...
//check for password length
...
//abort if errors are found
if (errors.length > 0) {
const info = {
errors: errors,
...,
};
console.log(`returning info ${info}`);
return done(null, info);
}
//form is filled in correctly create a user
else {
...
}
...
Random things I've tried so far:
Adding , form_validate behind info & changing the required variables to form_validate doesn't pass it through to the auth file.
There are probably better ways to handle form validation, haven't looked that up yet, if you have any suggestions please tell me, but I still kind of want to know if it would be possible to pass custom objects through passports methods.
Take a look at the example from the documentation:
passport.use(new LocalStrategy(
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.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
If you determine that the username/pass are not correct, you can call done with the last parameter being any object that you wish
return done(null, false, { message: "Incorrect password", otherData: "my other data"});
And, if it is a success, you can pass the user, but there is no reason you can't add more data to the user, or pass something completely different.
return done(null, {username: "user123", otherData: myData, customString: "myString"});

How to make auth with callback hell?

Create a auth system. When user make a post-query (router.js):
var user = require("../controllers/user");
router.post("/login", function(req, res, next){
user.login(req, function(result) {
res.send(result);
});
File controllers/user.js:
var mongo = require("./mongo");
exports.login = function (req, callback) {
var res;
var login = req.body.login,
password = req.body.password;
var user = new mongo.User({
login: login,
password: password
});
//check if exist user
user.findOne({
login: login
}, function (err, obj) {
if (err) {
console.log("user not found -> save");
user.save(function (err) {
if (err) {
console.log(err);
} else {
callback("OK");
}
});
} else {
console.log("ELSE");
if (password == obj["password"]) {
callback("OK");
}
}
});
}
I want when user.login will finished create/make new user -> call callback with result.
exports.login = function(req, callback){
user.findOne(/...login or sign up new user.../);
callback("Success");
}
So in case when user authorized ->
user.login(req, function(result) {
res.send(result); //this executes
});
How to make this?
The main problem was in syntax, when I tried to find user using user.findOne().
Instead user.findOne() I use mongo.User.findOne():
//mongo.User because I required a mongo.js, where connect to db
// where I had a User model
mongo.User.findOne({ login: login }, function(err, user){
if(err){
reject(err);
} else {
if(user) {
if(password == user["password"]) {
resolve("Login success.");
}
} else {
console.log("new user creating...");
var newUser = new mongo.User({ login: login, password: password });
newUser.save(function(err){
if(err) {
reject(err);
console.log("error with saving");
} else {
resolve("New user created.")
}
})
}
}
});
Also I have used a Promises (as said user Tomalak), so code in router.js:
function userCheck(req){
return new Promise(function(resolve, reject){
user.check(req, resolve, reject);
});
}
userCheck(req).then(result => {
console.log("promise result");
res.send(result);
}, error => {
console.log("promise error");
res.send(error);
});

async.each cannot set headers after already set

Here's what happening. I'm saving new companies first, then attaching the _id to each new user before they get saved. The issue I'm running into is returning a response. When I put the res.json() into the function thats getting repeated obviously I'm getting an error because I already have a response sent from the first time it loops through.
So, How do I call signupSeq(record, res) but wait for the async methods to finish so I know whether I have an error or not?
var signupSeq = function(req, res) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
return err
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
return result
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
var x = signupSeq(record, res)
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
You could add a callback (cb) in your signupSeg function.
var signupSeq = function(req, res, cb) {
async.waterfall([
function(callback) {
console.log(req);
if (req.company._id===undefined){
var company = new Company(req.company);
company.save(function(err){
if (err) {
console.log('save error');
callback(err);
}else{
callback(null, company._id);
}
})
}else{
callback(null, req.company._id); //pass teh plain ID if it's not a new name:xxx
}
},
function(companyId, callback) {
delete req.company
req.company = companyId
// Init Variables
var user = new User(req);
var message = null;
// Add missing user fields
user.provider = 'local';
user.displayName = user.firstName + ' ' + user.lastName;
// Then save the user
user.save(function(err) {
if (err) {
callback(err);
} else {
callback(null, user);
}
});
}
], function (err, result) {
if(err){
console.log(result+'funciton result')
cb(err)
// res.status(400).send({
// message: errorHandler.getErrorMessage(err)
// });
}else{
console.log(result+'funciton result')
cb(null,result)
//res.json(result)
}
});
}
exports.saveMany = function(req, res){
async.each(req.body, function(record, callback) {
// Perform operation on record.body here.
console.log('Processing record.body ' + record);
// Do work to process record.body here
signupSeq(record, res,function(err,result){
var x= result;
console.log(x+'<<<<<<<value of x');
console.log('record.body processed');
callback();
})
}, function(err){
// if any of the record.body processing produced an error, err would equal that error
if( err ) {
res.json(err);
// One of the iterations produced an error.
// All processing will now stop.
console.log('A record.body failed to process');
} else {
res.json('Success');
console.log('All files have been processed successfully');
}
});
}
This way inside the asyn.each the signipSeg will have to finish before the call of the callback().
Hope this helps.

Categories

Resources