Authentication module 'Passport' requires a FindOrCreate method in order to do a login. I am using mongoose in order to save my users with the following schema:
var UserSchema = new Schema({
firstname: String,
lastname: String,
email: String,
accounts: []
});
The accounts array holds objects that represent facebook accounts, like {provider: "facebook", uid: "someFacebookId"}
.
My authentication strategy looks like this:
// Authentication Strategy
passport.use(new FacebookStrategy({
clientID: CONFIG.fb.appId,
clientSecret: CONFIG.fb.appSecret,
callbackURL: CONFIG.fb.callbackURL
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
User.find({ 'accounts.uid': profile.id, 'accounts.provider': 'facebook' }, function(err, olduser) {
if(olduser._id) {
console.log('User: ' + olduser.firstname + ' ' + olduser.lastname + ' found and logged in!');
done(null, olduser);
} else {
var newuser = new User();
var account = {provider: "facebook", uid: profile.id};
newuser.accounts.push(account);
newuser.firstname = profile.name.givenName;
newuser.lastname = profile.name.familyName;
newuser.email = "TBD...";
newuser.save(function(err) {
if(err) { throw err; }
console.log('New user: ' + newuser.firstname + ' ' + newuser.lastname + ' created and logged in!');
done(null, newuser);
});
}
});
});
}
));
Problem: After querying my database (User.find(...)
) the callback function is executed immediately without waiting for my database to answer. This results in a undefined olduser
object. So I am getting a dublicate of the same user into my database every time this user tries to login.
How do I handle this asynchronous callback properly?
User.find
returns an array of documents that match your conditions. In your case you want to use User.findOne
instead, and then check if (olduser)...
to determine if a matching doc was found.
process.nextTick(function () {
var query = User.findOne({ 'fbId': profile.id });
query.exec(function (err, oldUser) {
console.log(oldUser);
if(oldUser) {
console.log('User: ' + oldUser.name + ' found and logged in!');
done(null, oldUser);
} else {
var newUser = new User();
newUser.fbId = profile.id;
newUser.name = profile.displayName;
newUser.email = profile.emails[0].value;
newUser.save(function(err) {
if(err) {throw err;}
console.log('New user: ' + newUser.name + ' created and logged in!');
done(null, newUser);
});
}
});
});
Hate to nitpick, but the other methods mentioned here break if two users try to signup at the same time-- before you go into production, you'll want to have a look into transactions: http://www.mongodb.org/display/DOCS/two-phase+commit
There's an app for that: mongoose-findorcreate