Nodejs, return condition not working [duplicate] - node.js

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 6 years ago.
I am new to Nodejs. I am trying to return data. after select query. Here i am writing two condition's. first is very simple in logic and it is working but let me know why second condition is not working.
First Condition:
var arr = {email:"john#gmail.com", password:"};
return arr;
databaseConnection.query("SELECT * FROM users where email = '"+email+"' and password = '"+password +"'", function (err, result) {
});
Second Condition
databaseConnection.query("SELECT * FROM users where email = '"+email+"' and password = '"+password +"'", function (err, result) {
var arr = {email:"john#gmail.com", password:"};
return arr;
});
from passport.js
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
var TwitterStrategy = require('passport-twitter').Strategy;
var configAuth = require('./auth');
module.exports = function(passport,databaseConnection) {
var usermodule = require('../models/user')(databaseConnection);
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use('local-login', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) { // callback with email and password from our form
var user={};
var result = usermodule.login(email,password);
console.log('usercraeted');
user["email"]=email;
user["status"]=true;
user["name"]="John Snow";
user["avatar"]="";
user["loginStatus"]=true;
return done(null, user);
user["msg"]="invalide email";
console.log("out");
return done(null, false,user["msg"]);
};
);
};

In your first example, the code is executed up to this point:
var arr = {email:"john#gmail.com", password:"};
return arr;
// interpreter exits and `{email:"john#gmail.com", password:"}` is returned,
// `databaseConnection.query("` is never executed
In your second example, the code is executed up to this point:
databaseConnection.query("SELECT * FROM users where email = '"+email+"' and password = '"+password +"'", function (err, result) {
var arr = {email:"john#gmail.com", password:"};
return arr;
});
// interpreter exits, undefined is returned

The second condition doesn't work because node.js is asynchronous. And your return is on a callback that take some times to be executed.

You have to use callbacks.
function getUser() {
var email = "test#test.com";
var password = "test";
getDatas(email, password, function(err, result) {
// the result is good and not undefined if no errors :)
});
}
function getDatas(email, password, callback) {
databaseConnection.query("SELECT * FROM users where email = '"+email+"' and password = '"+password +"'", function (err, result) {
callback(err, result); // callback is executed with the result you
});
}
Hope I helped you

Related

Web page keeps loading indefinitely if removing (req, res, next) on passport.authenticate(...)

I downloaded one sample project from internet. Below there are some fragments of the code:
On the routes file I have the following (just a fragment):
var authController = require('./controllers/authController'),
var passport = require('passport');
var authLoginFacebook =
passport.authenticate(
'facebook',
{
session: false,
scope: ['public_profile', 'email']
}
);
var checkJwt = function(req, res, next) {
passport.authenticate(
'jwt',
{session: false },
function (err, user, info) {
next();
}
)(req, res, next);
}
module.exports = function(app) {
// ...
app.get(
'/api/auth/login/facebook/callback',
checkJwt,
authLoginFacebook,
authController.login
);
// ...
}
On the passport file I have the following (just a fragment):
var User = require('../models/user');
var credentials = require('./credentials');
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var LocalStrategy = require('passport-local').Strategy;
var FacebookStrategy = require('passport-facebook').Strategy;
module.exports = function(passport) {
passport.use(
new JwtStrategy({
secretOrKey: credentials.secret,
jwtFromRequest: ExtractJwt.fromAuthHeaderWithScheme('JWT'),
},
function(payload, done) {
User.findById(
payload._id,
function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
}
);
}
)
);
var fbStrategy = credentials.facebook;
fbStrategy.passReqToCallback = true;
passport.use(new FacebookStrategy(fbStrategy,
function(req, token, refreshToken, profile, done) {
// asynchronous
process.nextTick(function() {
// check if the user is already logged in
if (!req.user) {
User.findOne({
'facebook.id': profile.id
}, function(err, user) {
if (err)
return done(err);
if (user) {
// if there is a user id already but no token (user was linked at one point and then removed)
if (!user.facebook.token) {
user.facebook.token = token;
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
user.facebook.email = (profile.emails[0].value || '').toLowerCase();
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
return done(null, user); // user found, return that user
} else {
// if there is no user, create them
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.token = token;
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
newUser.facebook.email = (profile.emails[0].value || '').toLowerCase();
newUser.save(function(err) {
if (err)
return done(err);
return done(null, newUser);
});
}
});
} else {
// user already exists and is logged in, we have to link accounts
var user = req.user; // pull the user out of the session
user.facebook.id = profile.id;
user.facebook.token = token;
user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
user.facebook.email = (profile.emails[0].value || '').toLowerCase();
user.save(function(err) {
if (err)
return done(err);
return done(null, user);
});
}
});
})
);
// ...
};
I have few questions here:
why on: passport.authenticate('jwt', ... are passed these arguments: (req, res, next) and on passport.authenticate('facebook', ... don't while they are used in the same line one next to other?
app.get(
'/api/auth/login/facebook/callback',
checkJwt,
authLoginFacebook,
authController.login
);
If I remove those arguments, then the web page keeps loading indefinitely.
why inside: passport.use(new FacebookStrategy is defined: req.user? where was declared the field: user for the object req?
Thanks!
*Edit: this is a function invoking another function...which is required because of the callback using next(). The facebook function doesn't have that.
In other words, when you invoke passport.authenticate, the return value is actually going to be a function expecting the parameters req, res, next. Normally you don't need to wrap it, because it just works. However, in this case there is a callback function being passed in as an argument, and that callback function needs access to the next parameter. So you have to wrap the whole thing to gain access to that next parameter.
*Note: the wrapped function isn't actually returning anything to/through the wrapping function. passport.authenticate() returns a function, and then this return function is self-invoked with the parameter group that follows. But this second self-invoked function result isn't captured as a variable or returned or anything.
The reason is that the important thing is either sending a response using the res parameter or allowing express to continue to the next layer of middleware/etc by calling the next() callback parameter. It's all happening asynchronously, and flow is directed using the callbacks.
var checkJwt = function(req, res, next) {
passport.authenticate(
'jwt',
{session: false },
function (err, user, info) {
next();
}
)(req, res, next);
}
the req.user would be a user from a previous login, which i believe passport.js normally stores using the express-session package.

Why can't I create and execute functions in my passport.js local strategy callback?

When I try calling saveTheUsr() it results in an error saying usr is not defined. I just don't get it and why it is not accessible? I know I can simply exclude the function and have the functions contents in the callback and it would work. I just want to know a way that I can declare the usr object and call that through a function in the callback. Exactly the way I tried in my callback. Is that possible? and why does my method not work?
var LocalStrategy = require('passport-local').Strategy;
var User = require('../app/models/user');
function saveTheUsr(){
var usr = new User();
usr.local.email = email;
usr.local.password = password;
usr.save(function(err) {
if (err)
throw err;
return done(null, usr);
});
}
passport.use('local-signup', new LocalStrategy({
usernameField : 'username',
passwordField : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ 'local.email' : email }, function(err, user) {
if (err)
return done(err);
if (user) {
return done(null, false);
} else {
saveTheUsr();
}
});
}));
As pointed in the comment.
The issue was with the done variable not being defined in the saveTheUsr() function. And the fix for it would be to make saveTheUsr accept an extra parameter called done and pass a callback function for places that calls it.

How to generate a unique id and save it to mongoDB in passportJS?

I am using passportJS, in my callback I would like to carry the following operations.
1) generate a random string
2) check the database if that id exists, if it exist then regenrate id untill it is unique.
3) Save the user model (with a unique id).
I tried writing multiple functions but it seems that the newUser object is undefined inside the functions!
Here is my functions I am using from my mongoose model.
userSchema.methods.generateVci = function(length, characters){
var string = '';
for(var i = length; i > 0; --i){
string += characters[Math.round(Math.random() * (characters.length - 1))];
}
return string;
};
userSchema.statics.validateVci = function(uniquekey){
this.find({}, function(err,user){
for(var i = 0; i < user.length; ++i){
var uservci = user[i].local.vci;
if(uservci == uniquekey){
console.log('false');
return false;
}
}
console.log('true');
return true;
});
};
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
var generatedVciKey = newUser.generateVci(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
var isvalidvci = User.validateVci(generatedVciKey);
if(isvalidvci){
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.vci = generatedVciKey;
}
// save the user
newUser.save(function(err) {
if (err)
throw err;
return done(null, newUser);
});
}
});
}));
If you could please show me a way to write some sort of recursive function in that passportjs would only save the user model when a unique ( checked with the database) id has been generated. The unique id must be regenerated again and again until it is truly unique from any other ids in the database. I have no clue how to do this as it seems that when I write a function the variables in the passportjs callback becomes undefined.
I rewrote this generateVci but you should really use node-uuid like someone already suggested
userSchema.methods.generateVci = function(length, characters) {
return characters.split('').map(function() {
var randomKey = Math.round(Math.random() * (characters.length - 1));
return characters[randomKey];
}).join('').substring(0, length);
};
Your validateVCI is async so you have to pass a callback or other way is to use promises
userSchema.statics.validateVci = function(uniquekey, cb){
this.find({}, function(err, users){
if (err) {
return cb(err);
}
var isInvalid = users.reduce(function(invalid, user) {
if (invalid) {
return true;
}
return user.local.vci === uniquekey;
}, false);
if (isInvalid) {
return cb(null, false);
}
console.log('true');
return cb(null, true);
});
};
You should make vci field unique in your database ...
So when you try to create a user with same vci it would fail
function createUser(email, password, done) {
var generatedVciKey = newUser.generateVci(32, '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ');
User.validateVci(generatedVciKey, function(isInvalid) {
if (isInvalid) {
return createUser(email, password, done)
}
var newUser = new User();
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.vci = generatedVciKey;
// save the user, or try again on error
newUser.save(function(err) {
if (err) {
return createUser(email, password, done);
}
done(null, newUser);
});
});
}
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
}, function(req, email, password, done) {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'local.email' : email }, function(err, user) {
// if there are any errors, return the error
if (err)
return done(err);
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
}
createUser(email, password, done);
});
}));
Do you realy need to do this? You can use node-uuid to generate unique identifier. So the probability that you will generate non-uniqe id negligible low

Create user, callback

1st post. I'm a student studying js and came across this problem with a callback that I'm not sure how to structure properly.
var bcrypt = require('bcrypt-nodejs');
var users = db.collection("users");
this.addUser = function(username, password, email, callback) {
"use strict";
// Generate password hash
var salt = bcrypt.genSaltSync();
var password_hash = bcrypt.hashSync(password, salt);
// Create user document
var user = {'_id': username, 'password': password_hash};
// Add email if set
if (email != "") {
user['email'] = email;
}
// NOT SURE What to write here
callback(Error("addUser Not Yet Implemented!"), null);
}
First of all: do not use sync methods if possible, use callbacks instead.
var bcrypt = require('bcrypt-nodejs');
var users = db.collection("users");
this.addUser = function(username, password, email, callback) {
"use strict";
// Generate password hash
bcrypt.genSalt(function(err, salt) {
if (err) {
callback(err);
return;
}
bcrypt.hash(password, salt, function(err, password_hash) {
if (err) {
callback(err);
return;
}
// Create user document
var user = {
'_id': username,
'password': password_hash
};
// Add email if set
if (email != "") {
user['email'] = email;
}
// NOT SURE What to write here
callback(null);
});
});
}
and please ask precise questions.
If I understand propperly, you dont know how to handle a callback?
you simply pass the function that will be called after all work in your function is done as the parameter callback. when your done, you call the callback with the wanted parameters.
here it's the err object in case of an error or null if there is no error.
If you want to pass your created user to the callback, just replace
callback(null); with callback(null, user);

How to passing data in TwitterStrategy, PassportJS?

I have three kind of user:
Viewer (link to sign in: auth/v/twitter)
Creator (link to sign in: auth/c/twitter)
Admin (link to sign in: auth/a/twitter)
And also I have 3 different db/collection
c_viewer
c_creator
c_admin
Where each kind of user have a different link to sign in.
Now let's take a look at the codes
var passport = require('passport')
,TwitterStrategy = require('passport-twitter').Strategy;
passport.use(new TwitterStrategy({
consumerKey: config.development.tw.consumerKey,
consumerSecret: config.development.tw.consumerSecret,
callbackURL: config.development.tw.callbackURL
},
function(token, tokenSecret, profile, done) {
process.nextTick(function(req, res) {
var query = User.findOne({ 'twId': profile.id});
query.exec(function(err, oldUser){
if(oldUser) {
done(null, oldUser);
} else {
var newUser = new User();
newUser.twId = profile.id;
newUser.twUsername = profile.username;
newUser.name = profile.displayName;
newUser.avatar = profile.photos[0].value;
-> newUser.age = req.body.creator.age; ???
newUser.save(function(err) {
if(err) throw err;
done(null, newUser);
});
};
});
});
}));
app.get('/auth/c/twitter', passport.authenticate('twitter'),
function(req, res) {
var userUrl = req.url;
// codes to pass the userUrl to TwitterStrategy
});
app.get('/auth/twitter/callback',
passportForCreator.authenticate('twitter', { successRedirect: '/dashboard', failureRedirect: '/' }));
And this is my form
<input type="text" name="creator[age]" placeholder="How old are you?">
<a id="si" class="btn" href="/auth/c/twitter">Sign in</a>
My questions:
1. Can We pass <input> data to the login process? so We can read the input data in TwitterStrategy, and save to the db
2. Can We get "c" from login url (auth/ c /twitter) and pass it to TwitterStrategy? so we can simply check in different db/collection and change the query.
The idea is to store your values before redirecting user on twitter for authentication, and re-use these values once the user came back.
OAuth2 includes the scope parameter, which perfectly suits that case. Unfortunately, TwitterStrategy is based on OAuth1. But we can tackle it !
The next trick is about when creating the user.
You should not do it when declaring strategy (because you cannot access input data), but a little later, in the last authentication callback
see here the callback arguments.
Declaring your strategy:
passport.use(new TwitterStrategy({
consumerKey: config.development.tw.consumerKey,
consumerSecret: config.development.tw.consumerSecret,
callbackURL: config.development.tw.callbackURL
}, function(token, tokenSecret, profile, done) {
// send profile for further db access
done(null, profile);
}));
When declaring your authentication url (repeat for a/twitter and v/twitter):
// declare states where it's accessible inside the clusre functions
var states={};
app.get("/auth/c/twitter", function (req, res, next) {
// save here your values: database and input
var reqId = "req"+_.uniqueId();
states[reqId] = {
database: 'c',
age: $('input[name="creator[age]"]').val()
};
// creates an unic id for this authentication and stores it.
req.session.state = reqId;
// in Oauth2, its more like : args.scope = reqId, and args as authenticate() second params
passport.authenticate('twitter')(req, res, next)
}, function() {});
Then when declaring the callback:
app.get("/auth/twitter/callback", function (req, res, next) {
var reqId = req.session.state;
// reuse your previously saved state
var state = states[reqId]
passport.authenticate('twitter', function(err, token) {
var end = function(err) {
// remove session created during authentication
req.session.destroy()
// authentication failed: you should redirect to the proper error page
if (err) {
return res.redirect("/");
}
// and eventually redirect to success url
res.redirect("/dashboard");
}
if (err) {
return end(err);
}
// now you can write into database:
var query = User.findOne({ 'twId': profile.id});
query.exec(function(err, oldUser){
if(oldUser) {
return end()
}
// here, choose the right database depending on state
var newUser = new User();
newUser.twId = profile.id;
newUser.twUsername = profile.username;
newUser.name = profile.displayName;
newUser.avatar = profile.photos[0].value;
// reuse the state variable
newUser.age = state.age
newUser.save(end);
});
})(req, res, next)
});

Resources