Node: Control flow, Can't save FB friends to db with mongoose - node.js

first off thank you for your help. I am trying to make a call to the facebook api to gather all of my users friends.
passport.use(new FacebookStrategy({
clientID: config.fb.id,
clientSecret: config.fb.secret,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({
"facebook.id" : profile.id
}, function(err, user) {
if (err) {
return done(err);
}
//No user was found... so create a new user with values from Facebook (all the profile. stuff)
if (!user) {
this is the call back I am made to return the data
function viewback(err, data, user) {
if(err) {
console.log("Error: " + JSON.stringify(err));
} else {
return data;
}
}
below is where I am making the call for friends, the data variable is not getting set before the call to create the user is being completed and it is not saving the data.
var fbapi = require('facebook-api');
var client = fbapi.user(accessToken); // do not set an access token
process.nextTick(function(){
var data = client.me.friends(viewback);
});
user = new User({
name: profile.displayName,
username: profile.username,
profileUrl: profile.profileUrl,
provider: 'facebook',
token: accessToken,
//now in the future searching on User.findOne({'facebook.id': profile.id } will match because of this next line
facebook: profile._json
fb_friends: data
});
user.save(function(err) {
if (err) console.log(err);
console.log('making a new user')
return done(err, user);
});
} else {
//found user. Return
console.log('found the user')
return done(err, user);
}
});
}));
I don't receive any errors and the user saves in the DB, but without the friend data. Any help would be great. Even better would be something I could read to pick up on this concept.

Related

Model Create returns undefined with no creation - Sails JS

I'm trying to implement the passport-facebook strategy into my application, however I seem to be running into some issues.
I can successfully call Facebook and get a response back, however when trying to save the user to my database, nothing happens.
In my code below, I want to check to see if the user already exists in the database, if they do, return their details, if they don't create a new entry.
passport.use(new FacebookStrategy({
clientID: '123',
clientSecret: '123',
callbackURL: 'http://localhost:1337/auth/facebook/callback',
profileFields: ['email']
},
function(accessToken, refreshToken, profile, done) {
User.findOne({ email: profile.emails[0].value}, function(err, user){
if (err) { return done(err); }
if (!user || user == 'false') {
console.log('No user found');
console.log(profile.id);
User.create({facebookId: profile.id, email: profile.emails[0].value}, function(err, user){
return done(null, user, { message: 'Facebook user created'});
})
} else {
console.log(user);
return done(null, user, { message: 'User already registered'});
}
})
}
));
I can log out the profile id fine, as well as the email, however the User.create statement does not run.
I also have passport-local implemented, and if I post the data to my user model, it creates the user just fine.
For reference, my user model looks like:
module.exports = {
connection: 'someMongodbServer',
attributes: {
facebookId: {
type: 'integer',
unique: true
},
email: {
type: 'email',
required: true,
unique: true,
},
password: {
type: 'string',
minLength: 6,
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate: function(user, cb) {
if(user.password){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(user.password, salt, function(err, hash){
if(err) {
console.log(err);
cb(err)
} else {
user.password = hash;
cb();
}
});
});
}
}
};
Does anyone know where I may be going wrong?
Managed to solve it, was a silly mistake on my behalf.
The User model invokes the beforeCreate function to hash the password. As I was using the Facebook strategy for Passport, there is no password for a Facebook user so I added an if statement to check for this.
if(user.password){
What I had missed was the callback in this function should their not be a password. Solution for the beforeCreate function is:
beforeCreate: function(user, cb) {
if(user.password){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(user.password, salt, function(err, hash){
if(err) {
console.log(err);
cb(err)
} else {
user.password = hash;
cb();
}
});
});
}
cb();
}

How can i switch to another user account from an admin account

I have a user table in which i have an Admin account and some other normal users accounts.
I want to do all activities which for a particular user. It should act like that activity done by same user.
Can somebody tell me how can i switch to another account from Admin account
without login to that account.
Currently i use passport authentication.(passport-local)
Here is my code
app.get('/secure/group/login', function(req,res,next) {
passport.authenticate('local',function(err,user,info) {
console.log("error is "+err);
req.logIn('tessAccount',function(err) {
console.log("Weer" +err);
});
console.log("dd");
})(req,res,next);
});
});
and passport code
var LocalStrategy = require('passport-local').Strategy;
module.exports = function(passport) {
// used to serialize the user for the session
passport.serializeUser(function(user, done) {
done(null, user.token);
});
passport.use(new BearerStrategy(
function(token, done) {
user.tokenExist(token, function(err, user) {
if (err) {
return done(err);
}
else {
return done(null, user, { scope: 'all' });
}
});
}
));
// used to deserialize the user
passport.deserializeUser(function(accessToken, done) {
user.getUserByAccessToken(accessToken, function(err, dbUser) {
if (err) {
done(err);
} else {
done(null, dbUser[0]);
}
});
});
// we are using named strategies since we have one for login and one for signup
// by default, if there was no name, it would just be called 'local'
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) {
user.emailExists(email, function(err, exists) {
if (err)
return done(err);
else if (exists) {
return done(null, false, {
message: req.flash('loginMessage')
});
} else {
req.userDetails.status = 0;
req.userDetails.token = md5.digest_s(req.userDetails.email + new Date());
req.userDetails.userImage = config.user.image;
user.register(req.userDetails, function(err, newUser) {
if (err)
return done(err);
else {
/*Get user Shared article if exist start*/
getSharedArticlesOfnewlyuserIfExist(email, newUser.insertId);
/*Get user Shared article if exist end*/
req.userDetails.id = newUser.insertId;
return done(err, req.userDetails);
}
});
}
});
}));
passport.use('local-login', 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) {
// callback with email and password from our form
// 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.matchPassword({
email: email,
password: password
}, function(err, newUser) {
// if there are any errors, return the error before anything else
if (err)
return done(err);
// if no user is found, return the message
if (newUser.length > 0) {
var user = {
id: newUser[0].user_id,
email: newUser[0].email_id,
token: newUser[0].user_access_token
};
return done(null, user);
} else {
return done(null, false, {
message: 'Incorrect username or password.'
}); // req.flash is the way to set flashdata using connect-flash
}
});
}));
User.findOne({ username: 'Myusername' }, function(err, user) {
req.logIn(user, function(err){});
});
This worked for me to log into an account without using password, it switches from my admin account to an user account.
Actually I'm using passport.js and mongoose

Nodejs - passport oauth2 authentification for express app with redis DB

I'm having trouble creating the Redis DB model for Passport Amazon oAuth2. I am using "redis-connect" module and I have got a running Redis local server. my redis server successsfully stores session data so the module and the server is running well. I only need help with the User variable.
Here is my code:
//create a user model
var User = new RedisStore({ host:'127.0.0.1', port:6379, prefix:'token' });
passport.serializeUser(function(user, done) {
console.log('serializeUser: ' + user._id);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user){
console.log(user);
if(!err){ done(null, user);}
else {done(err, null);}
});
});
passport.use(new AmazonStrategy({
clientID: "foo",
clientSecret: "SECRET",
callbackURL: "https://localhost/auth/amazon/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOne({ oauthID: profile.id }, function(err, user) {
if(err) { console.log(err); }
if (!err && user !== null) {
done(null, user);
} else {
var user = new User({
oauthID: profile.id,
name: profile.displayName,
created: Date.now()
});
user.save(function(err) {
if(err) {
console.log(err);
} else {
console.log("saving user ...");
done(null, user);
}
});
}
});
}
));
Also the findById function is not defined. I have no idea how... I believed it was part of the redis-connect module. Can anyone help me out here?

How to differentiate Login from Sign up when using PassportJS for Facebook

I am trying to set up goals on Google Analytics to track Sign Ups, so I set up a 'thank you ' page as my url goal. It works well when my users sign up with their email address but not when they use facebook to sign up/login. When they login, they are redirected to the thank you page as there is only one url callback when setting up Facebook using Passport JS and Node.
Here is my code:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
UserActivity.findOne(id,'uid ref', function (err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: 'XXXXXXXXXXXX',
clientSecret: 'XXXXXXXXXXXXXXXX',
callbackURL: "https://www.xxxxxxx.com/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
//User exists: we are done
if(uInfo){
done(err, uInfo);
}
else{
//User doesn't exist: we create a new one
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/thankyou',
failureRedirect: '/login' }));
If the user exists, I would like to redirect to their dashboard ('/dashboard) and if they are new users, I need to redirect them to /thankyou.
Any idea how to achieve this?
Thanks a lot!
Nevermind, found the answer. Here is the updated code below. Pay attention to the use of passReqToCallback and req.session.newu
passport.use(new FacebookStrategy(
{
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "https://www.XXX.co/auth/facebook/callback",
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
if(uInfo){
done(err, uInfo);
}
else{
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
req.session.newu=true;
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',function(req, res, next) {
passport.authenticate('facebook', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
var redLink = '/dashboard';
if(req.session.newu)redLink = '/dashboard?newu'
return res.redirect(redLink);
});
})(req, res, next);
});
An existing user will be redirected to /dashboard and a new user will be redirected to /dashboard?newu
Google Analytics doesn't need 2 different urls, it just needs a query string. When I set up the url goal, I selected url start with /dashboard?newu.
Hope this helps
The question is a bit old but it might still help someone like me. The OP's answer works but it means you have to take care of log-in user and session, etc. In case you still want to leave those work to PassportJS, use req.session.returnTo in strategy's callback with successReturnToOrRedirect option in passport.authenticate() would work.

Facebook OAuth creates new user each time using Passport and Express

I am using the Passport node module in conjunction with Express to enable users to login via Facebook, Twitter, and Google. I have setup Facebook and Twitter so far and users can login successfully, but it creates a new user every time even though I have tried to make it recognize when a user has already created an account. Here is the code I am using:
passport.use(new FacebookStrategy({
clientID: '********************',
clientSecret: '***********************',
callbackURL: "http://www.wundertutor.com:3000/auth/facebook/callback"
}, function(accessToken, refreshToken, profile, done) {
processProfile(profile, function(err, user) {
if (err) throw err;
done(null, user);
});
}));
function processProfile(profile, callback) {
if (profile.emails) {
profile.emails.forEach(function(email) {
findUserByEmail(email.value, function(err, user) {
if (user) {
return callback(null, user);
}
});
});
var newUser = {
id: profile.id,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
email: profile.emails[0].value
};
user.create(newUser, profile.provider, function(err, user) {
if (err) throw err;
return callback(null, user);
});
}
}
function findUserByID(id, callback) {
pool.getConnection(function(err, connection) {
var query = connection.query("SELECT * FROM users WHERE id = ?", id, function(err, rows) {
connection.end();
var user;
if (rows.length == 1) {
user = {
id: rows[0].id,
role: rows[0].role,
firstName: rows[0].firstName,
lastName: rows[0].lastName,
email: rows[0].email
};
}
return callback(null, user);
});
});
}
function findUserByEmail(email, callback) {
if (email) {
pool.getConnection(function(err, connection) {
if (err) throw err;
var query = connection.query('SELECT * FROM users WHERE email = ?', email, function(err, rows) {
connection.end();
if (err) throw err;
if (rows.length == 1) {
console.log("C");
return callback(null, rows[0]);
} else {
return callback(null, null);
}
});
});
} else {
return callback(null, null);
}
}
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
findUserByID(id, function(err, user) {
done(null, user);
});
});
In processProfile, I have it loop through each email associated with a user profile and check to see whether that user is already in our database. If they are, then it gets that user's info and passes it to the 'done' callback to be serialized and log the user in. The strange thing is that when a user who is already in our database is logged in, it does get to this part of the code and returns the user successfully (determined by a series of console.logs), but for some reason it still creates a new user in the database each time they try to log in.
Also, on a side note, when I log in normally (via username and password), I am redirected to our learn page '/learn', but when I log in via Facebook, I am redirected to the learn page with some extra stuff afterwards '/learn#='. Any ideas?
Try rewriting your processProfile function to something like this:
function processProfile(profile, callback) {
var result;
if (profile.emails) {
profile.emails.forEach(function(email) {
findUserByEmail(email.value, function(err, user) {
if (user) {
result = user;
}
});
});
if( !user ) {
var newUser = {
id: profile.id,
firstName: profile.name.givenName,
lastName: profile.name.familyName,
email: profile.emails[0].value
};
user.create(newUser, profile.provider, function(err, user) {
if (err) throw err;
result = user;
callback( null, result );
});
} else {
callback( null, result );
}
}
}

Resources