I'm trying to setup user authentication with passportJS. I decided to use this npm package called mongoose-findorcreate instead of writing a findOrCreate function to work with passport on my own.
So now I am testing my app, and I can login using my facebook credentials as expected, but the issue is that I am getting multiple entries in my database for the same facebook login.
This is my user model:
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let findOrCreate = require('mongoose-findorcreate');
// User schema definition
let UserSchema = new Schema(
{
provider: { type: String, required: true },
id: { type: String, default: 'unknown' },
displayName: { type: String, default: 'unknown' },
name: { type: Object },
emails: { type: Array },
photos: { type: Array },
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now },
}
);
UserSchema.plugin(findOrCreate);
// Sets the updatedAt parameter equal to the current time
UserSchema.pre('save', (next) => {
now = new Date();
if(!this.updatedAt) {
this.updatedAt = now;
}
next();
});
module.exports = mongoose.model('user', UserSchema);
These are my routes:
let passport = require('passport');
let FacebookStrategy = require('passport-facebook').Strategy;
let User = require('../models/user');
passport.use(new FacebookStrategy({
clientID: 12345,
clientSecret: 'supersecretsecret',
callbackURL: 'http://localhost:1337/auth/facebook/callback'
},
(accessToken, refreshToken, profile, done) => {
User.findOrCreate( profile, (err, user) => {
if (err) {
return done(err);
}
done(null, user);
})
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
function redirectFB() {
return passport.authenticate('facebook');
}
function callbackFB() {
return passport.authenticate('facebook', {
successRedirect: '/word',
failureRedirect: '/'
})
}
module.exports = { redirectFB, callbackFB };
An example of duplicated users:
{
"_id":ObjectId("584ade8915644f1186fa8a96"),
"provider":"facebook",
"updatedAt": ISODate("2016-12-09T16:40:41.921 Z"),
"createdAt": ISODate("2016-12-09T16:40:41.921 Z"),
"photos":[ ],
"emails":[ ],
"displayName":"Miha Šušteršič",
"id":"10154914634234238",
"__v":0
}{
"_id":ObjectId("584adea3b8adfb11948ed1d6"),
"provider":"facebook",
"updatedAt": ISODate("2016-12-09T16:41:07.626 Z"),
"createdAt": ISODate("2016-12-09T16:41:07.625 Z"),
"photos":[ ],
"emails":[ ],
"displayName":"Miha Šušteršič",
"id":"10154914634234238",
"__v":0
}
I can't figure out if this is an issue with my code (passing the profile to the findOrCreate
function or not.
The reason why findOrCreate
will not work is because findOrCreate
query is passing the entire profile
object into the query criteria.
If you look here: https://github.com/drudge/mongoose-findorcreate/blob/master/index.js#L22
Depending on what type of data you get back from Facebook, your query will always return empty unless the profile object matches your schema EXACTLY.
What you can do here is this:
User.findOrCreate({ provider: profile.provider, id: profile.id, .... }, (err, user) => {
if (err) {
return done(err);
}
done(null, user);
})
instead of profile
pass in { provider: profile.provider, id: profile.id, .... }
of course, fix the { provider: profile.provider, id: profile.id, .... }
to be matched with your schema definition.