How to deal with async. findOrCreate method for passport and mongoose

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