2012-06-22 7 views
5

Das Authentifizierungsmodul 'Passport' erfordert eine FindOrCreate-Methode, um eine Anmeldung durchzuführen. Ich bin mit Mungo, um meine Benutzer mit dem folgenden Schema zu speichern:Wie mit Async umzugehen. findOrCreate-Methode für Pass und Mungo

var UserSchema = new Schema({ 
    firstname: String, 
    lastname: String, 
    email: String, 
    accounts: [] 
}); 

Die Konten Array-Objekte enthält, die Facebook-Konten darstellen, wie {provider: "facebook", uid: "someFacebookId"}.

Meine Authentifizierungsstrategie sieht wie folgt aus:

// 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: Nach meiner Datenbank abfragt (User.find(...)) die Callback-Funktion sofort ausgeführt wird, ohne zu warten, für meine Datenbank zu beantworten. Dies führt zu einem undefinierten Objekt olduser. Daher erhalte ich jedes Mal, wenn dieser Benutzer versucht, sich anzumelden, eine Kopie desselben Benutzers in meine Datenbank.

Wie gehe ich richtig mit diesem asynchronen Rückruf um?

+0

ich weiß, dass dies nicht direkt auf die Frage bezieht, aber das ist nicht ein wenig gefährlich Abfrage finden? Es sucht nach einem Benutzer mit einer beliebigen accounts.uid des angegebenen Wertes und mit jedem accounts.provider von 'facebook'. Aber was zwingt sie dazu, dasselbe Element der Kontenliste zu sein? Was ist, wenn ein anderer Benutzer eine passende UID mit einem anderen Provider hat? – StevenC

+0

Ich gehe davon aus, dass es die Kombination beider Werte sucht, die einzigartig sein sollte. – Sven

+1

Diese Annahme ist Gefahr. Weil in einem Array von Accounts gefunden wird, die übereinstimmen, wenn der Benutzer einen Facebook-Account hat und der Account * ANY * diesen Account hat. Wenn jemand einen OpenAuth-Server hat, kann er sich als beliebiger Benutzer anmelden, indem er die gewünschte Benutzer-ID zurückgibt. – tangxinfa

Antwort

4

User.find gibt ein Array von Dokumenten zurück, die Ihren Bedingungen entsprechen. In Ihrem Fall möchten Sie stattdessen User.findOne verwenden und dann if (olduser)... überprüfen, um festzustellen, ob ein übereinstimmendes Dokument gefunden wurde.

+0

Danke. Das hat eine Weile gedauert. Danke :-) – Sven

+1

Heads-up, bevor Sie in die Produktion gehen, werden Sie in Transaktionen schauen wollen: http://www.mongodb.org/display/DOCS/two-phase+commit Sonst zwei Benutzer, die sich gleichzeitig mit demselben Benutzernamen anmelden, werden das System beschädigen. Offensichtlich nicht zu sehr ein Problem * nur * für Facebook, da Benutzernamen sind bereits einzigartig, aber es wird mehr eine Rolle spielen, wie Sie Ihr Authentifizierungssystem mit anderen Strategien zu erweitern. – mikermcneil

1
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); 
      }); 
     } 
     }); 
    }); 
+2

Einige Erklärungen zu diesem Code wären nett. –

+0

Zuerst fragen wir unsere Datenbank ab, ob es einen alten Benutzer gibt, ob es einen alten Benutzer gibt, den wir erledigt haben, und wenn es keinen Benutzer in der Datenbank gibt, erstellen wir einen neuen Benutzer und speichern ihn in unserer Datenbank. – diesel

Verwandte Themen