2012-06-22 36 views
5

El módulo de autenticación 'Passport' requiere un método FindOrCreate para iniciar sesión. Estoy usando mangosta con el fin de salvar a mis usuarios con el siguiente esquema:Cómo lidiar con la asincronización. Método findOrCreate para pasaporte y mangosta

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

La matriz de cuentas contiene objetos que representan cuentas de facebook, como {provider: "facebook", uid: "someFacebookId"}.

Mi estrategia de autenticación es el siguiente:

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

Problema: Después de la consulta de mi base de datos (User.find(...)) la función de devolución de llamada se ejecuta inmediatamente sin esperar a mi base de datos para responder. Esto da como resultado un objeto olduser indefinido. Así que estoy recibiendo una copia del mismo usuario en mi base de datos cada vez que este usuario intenta iniciar sesión.

¿Cómo manejo esta devolución de llamada asincrónica correctamente?

+0

Sé que esto no está directamente relacionado con la pregunta, pero ¿no es esa consulta algo peligrosa? Busca un usuario con cualquier account.uid del valor dado y con cualquier accounts.provider de 'facebook'. Pero, ¿qué los fuerza a ser el mismo elemento de la lista de cuentas? Es decir, ¿qué pasaría si otro usuario tuviera un uid correspondiente con un proveedor diferente? – StevenC

+0

Supongo que está buscando la combinación de ambos valores, que deben ser únicos. – Sven

+1

Esto asumiendo es un peligro. Porque encuentra en una matriz de cuentas emparejadas si el usuario tiene una cuenta de Facebook y * CUALQUIER * cuenta tiene ese uid. Si alguien tiene un servidor OpenAuth, entonces puede iniciar sesión como cualquier usuario devolviendo el uid que desea. – tangxinfa

Respuesta

4

User.find devuelve matriz de documentos que coinciden con sus condiciones. En su caso, desea usar User.findOne en su lugar, y luego marcar if (olduser)... para determinar si se encontró un documento coincidente.

+0

Gracias. Eso me tomó un tiempo ahora. Gracias :-) – Sven

+1

Cabezas, antes de entrar en producción, querrá echarle un vistazo a las transacciones: http://www.mongodb.org/display/DOCS/two-phase+commit De lo contrario, dos los usuarios que se registren al mismo tiempo con el mismo nombre de usuario romperán el sistema. Obviamente no es un problema * solo * para Facebook, ya que los nombres de usuario ya son únicos, pero desempeñará un papel más importante a medida que aumente su sistema de autenticación con otras estrategias. – 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

Alguna explicación sobre este código sería agradable. –

+0

primero consultamos nuestra base de datos si hay un usuario anterior, si hay un usuario antiguo que llamamos hecho y si no hay un usuario en la base de datos creamos un nuevo usuario y lo guardamos en nuestra base de datos. – diesel

Cuestiones relacionadas