2012-06-22 10 views
5

認証モジュール 'Passport'は、ログインを行うためにFindOrCreateメソッドが必要です。私は、次のスキーマと私のユーザーを保存するためにマングースを使用しています:非同期の処理方法。パスポートとマングースのfindOrCreateメソッド

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

アカウントの配列は{provider: "facebook", uid: "someFacebookId"}のように、Facebookのアカウントを表すオブジェクトを保持しています。

マイ認証戦略は次のようになります。

// 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); 
      }); 
      } 
     }); 
    }); 
    } 
)); 

問題:を私のデータベース(User.find(...))コールバック関数が私のデータベースが応答するのを待つことなく、すぐに実行された後に照会します。これにより、定義されていないolduserオブジェクトが生成されます。だから私はこのユーザーがログインしようとするたびに、同じユーザーの別名を自分のデータベースに入れています。

この非同期コールバックを適切に処理する方法を教えてください。

+0

私はこれが質問に直接関係しているわけではありませんが、それは少し危険ですか?指定された値のaccounts.uidを持つユーザーと、「facebook」のaccounts.providerを持つユーザーを探します。しかし、それらを同じアカウントリスト要素にするのはなぜですか?つまり、別のユーザーが別のプロバイダと一致するUIDを持っていたらどうでしょうか? – StevenC

+0

私は両方の値の組み合わせを探していると仮定していますが、これは一意でなければなりません。 – Sven

+1

これは危険であると仮定します。なぜなら、ユーザーがFacebookアカウントを持っていて、* ANY *アカウントにそのuidがある場合、アカウントの配列が一致するからです。誰かがOpenAuthサーバを持っているなら、彼は望みのユーザIDを返すことで任意のユーザとしてログインできます。 – tangxinfa

答えて

4

User.findあなたの条件に一致するドキュメントのを返します。あなたの場合はUser.findOneを代わりに使用し、if (olduser)...をチェックして一致する文書が見つかったかどうかを確認してください。

+0

ありがとうございます。それは今、私にしばらく時間がかかりました。ありがとうございます:-) – Sven

+1

生産に入る前に、トランザクションを調べることをお勧めします。http://www.mongodb.org/display/DOCS/two-phase+commit それ以外の場合は、2つ同じユーザー名で同時にサインアップしたユーザーは、システムを破壊します。ユーザー名はすでにユニークなので、明らかにFacebookのための問題はあまりにも多くありませんが、他の戦略で認証システムを強化すると、より多くの役割を果たします。 – 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

このコードについての説明がうまくいくでしょう。 –

+0

まず、oldUserがある場合はデータベースにクエリを実行し、完了したoldUserが存在する場合はデータベースにユーザーが存在しない場合は、新しいユーザーを作成してデータベースに保存します。 – diesel

3

はnitpickする嫌いが、ここでは言及された他の方法には、2人のユーザーがあなたが生産に入る前に、同じtime--にサインアップしようとした場合、あなたがトランザクションに見てしたいと思う破る:http://www.mongodb.org/display/DOCS/two-phase+commit

関連する問題