功能User.findOrCreate在passport中的作用是什么,何时被调用?

53

我找不到有关这个函数的文档,因此无法正确地使其工作。该函数何时被调用,它在做什么,并且将什么作为第一个参数?我正在尝试从护照获取访问令牌,但无论如何都无法访问它。

passport.use(new FacebookStrategy({
    clientID:   APP_ID,
    clientSecret:   APP_SECRET,
    callbackURL: "http://localhost:3000/",
  },
  function(accessToken, refreshToken, profile, done) {
    User.findOrCreate({// what are these parameters?}, function (err, user) {
        // when is this function called and what is it doing? 
       });

  }
));

我如何从Passport获取访问令牌?

2个回答

88

User.findOrCreate 是一个虚构的函数,代表你用于通过 Facebook ID 查找用户或在用户不存在时创建用户的任何函数。我认为你的第一个问题是你的回调 URL 只是指向了你的根目录,所以你可能永远无法到达那个函数。

你的回调 URL 应该像这样:http://localhost:3000/auth/facebook/callback

然后处理该 URL:

app.get('/auth/facebook/callback', 
  passport.authenticate('facebook', { failureRedirect: '/login' }),
  function(req, res) {
    res.redirect('/');
  });

此时认证已经完成。将 accessToken 返回给您--“每次应用程序代表某个人调用 API 读取、修改或写入其特定 Facebook 数据时都需要此令牌”。您应该将其保存在某个表中,该表会存储用户的访问令牌。 profile 是另一个关键变量,因为它包含有关用户的信息(信息取决于服务)。

在该函数内部您可以自由操作。所以,请自行创建 User.findOrCreate。以下是来自 Facebook 的 passport 代码,并添加了一些注释以进行解释。这假设您正在使用类似 MongoDB 的东西并且有一个 User 表 (在此情况下,User 就是您声明的任何可以与 User 表接口的变量)。

//Use facebook strategy
passport.use(new FacebookStrategy({
        clientID: config.facebook.clientID,
        clientSecret: config.facebook.clientSecret,
        callbackURL: config.facebook.callbackURL
    },
    function(accessToken, refreshToken, profile, done) {
        //check user table for anyone with a facebook ID of profile.id
        User.findOne({
            'facebook.id': profile.id 
        }, function(err, user) {
            if (err) {
                return done(err);
            }
            //No user was found... so create a new user with values from Facebook (all the profile. stuff)
            if (!user) {
                user = new User({
                    name: profile.displayName,
                    email: profile.emails[0].value,
                    username: profile.username,
                    provider: 'facebook',
                    //now in the future searching on User.findOne({'facebook.id': profile.id } will match because of this next line
                    facebook: profile._json
                });
                user.save(function(err) {
                    if (err) console.log(err);
                    return done(err, user);
                });
            } else {
                //found user. Return
                return done(err, user);
            }
        });
    }
));

就我个人而言,我也使用“会员”表来跟踪每个用户的多个帐户(以便他们可以使用多个帐户进行身份验证),因为我是通过mongoose设置它的。实际上,这就是我存储访问令牌的地方。我更喜欢这种方法,而不是在用户表中添加Facebook列......但这取决于你。

var mongoose = require('mongoose'),
    Schema = mongoose.Schema,
    ObjectId = Schema.ObjectId;

var membershipSchema = new Schema({
    provider:  String,
    providerUserId:  String,
    accessToken: String,
    userId: {type: ObjectId, ref: 'User'},
    dateAdded: {type: Date, default: Date.now}
});

module.exports = mongoose.model('Membership', membershipSchema);

因此,我的User.findOrCreate版本从以下内容开始:

function(accessToken, refreshToken, profile, done) {
    Membership.findOne({
        providerUserId: profile.id
    }, function(err,membershipData) {
            //blah blah blah

其中成员身份是上述模型,并被定义为变量,如下:

var Membership =  require('./models/membership.js')

我正在尝试让它为我工作,但是它没有。你说得对,我从来没有调用我的回调函数。你能告诉我为什么这个函数(在你的情况下是Membership.findOne({..))需要在代码中吗?我试着不加它,但似乎它永远不会终止。 - Tommz
1
@Tommz 在代码中保存档案信息到数据库。最简单的形式是在 function(accessToken...) 中尝试返回 return done(null, profile);,因为它需要返回一些东西,否则就像你说的那样,它似乎永远不会终止。 - MikeSmithDev
是的,但当我想在像这样的代码中执行此操作并收到此错误时,问题就会出现:http://stackoverflow.com/questions/20436387/typeerror-cannot-read-property-id-of-undefined-at-strategy-app-get-passport-a。另外,我已经尝试在function(accessToken ...)中返回done(null,profile),但然后我会收到“对象”不能用作函数的错误,我猜是指“完成”:\ - Tommz
@MikeSmithDev 这可能是一个愚蠢的问题,但您是否通过电子邮件确定同一用户是否已经使用另一个社交提供商注册(因此只有当提供商返回与社交账户相同的电子邮件地址时,您才知道它与已经在您的数据库中的用户相同)? - Maxim Zubarev
1
@MaximZubarev 是的,那确实是我所做的。如果没有找到提供者ID,则下一步检查电子邮件以确保您不会为现有成员创建另一个帐户。虽然上面的代码中没有包括它,但将在 if (!user) { 部分中包含它。 - MikeSmithDev

15
如果您想使用findOrCreate,请尝试npm包mongoose-findorcreatesupergoose
例如:mongoose-findorcreate
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost');

var findOrCreate = require('mongoose-findorcreate')
var Schema = mongoose.Schema;
var UserSchema = new Schema({ facebookId: Number});
UserSchema.plugin(findOrCreate);
var User = mongoose.model('User', UserSchema);

passport.use(new FacebookStrategy({
        clientID: 'clientID',
        clientSecret: 'clientSecret',
        callbackURL: "/auth/facebook/callback"
    },
    function(accessToken, refreshToken, profile, cb) {
        User.findOrCreate({ facebookId: profile.id }, function (err, user) {
          console.log('A new uxer from "%s" was inserted', user.facebookId);
          return cb(err, user);
        });
    }
));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接