用于处理本地和社交身份验证提供程序的Mongoose用户模型

时间:2017-03-05 13:29:07

标签: facebook express mongoose oauth passport.js

所以我正在使用Express(使用Mongoose和Passport)应用程序,我想将facebook作为身份验证方法。我已经完成了它并且它有效,但我认为我没有适当的用户模型来处理多个社交提供商的身份验证过程。我想合并不同的社交帐户。这是我目前的用户模型,适用于facebook auth:

let userSchema = mongoose.Schema({
    email: { type: String, unique: true },
    name: { type: String },
    password: { type: String },
    facebookId: String,
    facebookToken: String
}, { timestamps: true });

我想到了以下两种方法,但我不确定它们是否可行,哪种方式最灵活,最独立于社交提供者。 我想有这样的本地和社交数组:

let userSchema = mongoose.Schema({
    local: {
        email: { type: String, unique: true },
        name: { type: String },
        password: { type: String },
    },
    facebook: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    google: {
        id: String,
        token: String,
        email: String,
    }
}, { timestamps: true });

第三种方法是覆盖社交提供者ID(我不确定这是否合适)。

let userSchema = mongoose.Schema({
        email: { type: String, unique: true },
        name: { type: String },
        id: String,
        token: String,
    }, { timestamps: true });

1 个答案:

答案 0 :(得分:5)

所以我为自己找到了一个可行的解决方案,可以帮助其他人解决同样的问题。在我的用户模型中,我有我常用的字段,对于每个社交提供者,我都有一个像这样的单独数组(users / User.js):

let userSchema = mongoose.Schema({
    email: { type: String, unique: true },
    name: { type: String },
    password: { type: String },
    roles: [String],
    confirmation_code: String,
    confirmed: { type: Boolean, default: false },
    facebook: {
        id: String,
        token: String,
        email: String,
        name: String
    },
    google: {
        id: String,
        token: String,
        email: String,
        name: String
    }

}, { timestamps: true });

在与社交提供商进行身份验证时,我会额外检查是否已存在具有相同电子邮件的用户。如果它没有,我创建一个新用户。如果是这样,我只是将社交提供者数据(id,token等)添加到现有的用户数组中,如此(config / passport.js):

passport.use(new FacebookStrategy({
        clientID: oauth.facebook.clientID,
        clientSecret: oauth.facebook.clientSecret,
        callbackURL: oauth.facebook.callbackURL,
        profileFields: ['id', 'emails', 'name']
    },
        function (accessToken, refreshToken, profile, done) {
            process.nextTick(function () {
                User.findOne({
                    $or: [
                        { 'facebook.id': profile.id },
                        { 'email': profile.emails[0].value }
                    ]
                }, function (err, user) {
                    if (err) {
                        return done(err);
                    }

                    if (user) {
                        if (user.facebook.id == undefined) {
                            user.facebook.id = profile.id;
                            user.facebook.token = accessToken;
                            user.facebook.email = profile.emails[0].value;
                            user.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
                            user.save();
                        }

                        return done(null, user);

                    } else {
                        let newUser = new User();
                        newUser.facebook.id = profile.id;
                        newUser.facebook.token = accessToken;
                        newUser.facebook.email = profile.emails[0].value;
                        newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName;
                        newUser.name = profile.name.givenName + ' ' + profile.name.familyName;
                        newUser.email = profile.emails[0].value;

                        newUser.save(err => {
                            if (err) {
                                console.log(err);
                                throw err;
                            }

                            return done(null, newUser);
                        });
                    }
                });
            });
        }
    ));

使用此方法,您可以将一个配置文件与多个社交提供商连接。然而,有一个缺点。如果用户通过社交提供商首次注册新的个人资料,他将无法获得密码,因为社交提供商不会回复密码数据(duh)。他之后只需要通过他的个人资料更改(设置)他的密码。