Animesh Singh
Animesh Singh

Reputation: 9322

Facebook Passport Strategy returning 500 error

I am trying to make a Logged in or Signed In user to connect their account with facebook using passport.js facebook strategy, and save their profile photo, id, gender, timeline cover and token according the userSchema (as made in user.jsmodel shown below.

I tried many combinations but still either getting 500error from facebook, or if showing facebook auth, facebook can't return (the code combination, I tried) and save the object.

PS : I had entered correct callback URL in facebook

PPS: Please refer my UPDATED routes.js and updated passport.js below.

This is my routes.js file:

    app.get('/auth/connect/facebook', passport.authenticate('facebook-connect', { authType: 'rerequest', scope: ['id', 'cover', 'gender', 'photos'] }));

app.get('/auth/connect/facebook/callback',
    passport.authenticate('facebook-connect', {
        successRedirect: '/profile/configure',
        failureRedirect: '/profile/congigure'
            // failureFlash: true
    }));

My passport.js file of facebook-connect:

passport.use('facebook-connect', new FacebookStrategy({
        clientID: configAuth.facebookAuth.clientID,
        clientSecret: configAuth.facebookAuth.clientSecret,
        callbackURL: configAuth.facebookAuth.callbackURL,
        profileFields: ['id', 'cover', 'gender', 'photos'],
        enableProof: true
    },
    function(token, refreshToken, profile, cb) {
        process.nextTick(function() {
            User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
                if (err)
                    return cb(err);

                if (user) {
                    return cb(null, false, req.flash('fbflash', 'This facebook user is already connected with an account at eBird.'));
                } else {

                    user.local.facebook.id = profile.id;
                    user.local.facebook.token = token;
                    user.local.profile.gender = profile.gender;
                    user.local.profile.herobg = profile.cover;
                    user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
                    if (user.local.profile.dp == '') {
                        if (user.local.profile.gender == 'male') {
                            user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
                        }
                        if (user.local.profile.gender == 'female') {
                            user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
                        }
                    }

                    user.save(function(err) {
                        if (err)
                            throw err;
                        return cb(null, user);
                    });

                }
            });
        });
    }));

My user.js model:

var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var DateOnly = require('mongoose-dateonly')(mongoose);
var shortid = require('shortid');
var uniqueValidator = require('mongoose-unique-validator');

var userSchema = mongoose.Schema({
    _id: {
        type: String,
        default: shortid.generate
    },
    local: {
        email: String,
        username: { type: String, unique: true },
        firstname: String,
        surname: String,
        name: String,
        role: { type: String, default: 'user' },
        department: String,
        pno: Number,
        password: String,
        verified: { type: Boolean, default: false },
        profile: {
            dp: String,
            createdAt: { type: Date, default: Date.now },
            herobg: String,
            location: String,
            website: String,
            gender: String,
            birthday: DateOnly,
            lastlogin: { type: Date },
            notifications: {
                name: String,
                namedp: String,
                type: { type: String },
                date: { type: Date, default: Date.now },
                read: { type: Boolean, default: false }
            }
        },
        facebook: {
            id: String,
            token: String
        }
    }
});

userSchema.plugin(uniqueValidator, { message: '{Path}:{VALUE} is already taken.' });

userSchema.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

userSchema.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.local.password);
};

// userSchema.methods.bellTimesAgo = function(date);

module.exports = mongoose.model('User', userSchema);

The error, it's throwing me:

The www.facebook.com page isn’t working

www.facebook.com is currently unable to handle this request.
HTTP ERROR 500

Any help would be appreciated, Thanks.


UPDATE - 1

I read (& from passportjs docs) about passport.authorize() and updated my passport.js file accordig to passport.authorize() and also updated my routes, but still the same problem.

Here is my updated passport.js:

// Facebook Strategy Updated using authorize

passport.use(new FacebookStrategy({
        clientID: configAuth.facebookAuth.clientID,
        clientSecret: configAuth.facebookAuth.clientSecret,
        callbackURL: configAuth.facebookAuth.callbackURL,
        // profileFields: ['id', 'cover', 'gender', 'photos'],
        // enableProof: true,
        passReqToCallback: true
    },
    function(req, accessToken, refreshToken, profile, done) {
        process.nextTick(function() {

            if (!req.user) {
                User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
                    if (err)
                        return done(err);

                    if (user) {
                        return done(null, false, req.flash('fbflash', 'This facebook user is already connected with an account at eBird.'));
                    } else {

                        user.local.facebook.id = profile.id;
                        user.local.facebook.token = accessToken;
                        user.local.profile.gender = profile.gender;
                        user.local.profile.herobg = profile.cover;
                        user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
                        if (user.local.profile.dp == '') {
                            if (user.local.profile.gender == 'male') {
                                user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
                            }
                            if (user.local.profile.gender == 'female') {
                                user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
                            }
                        }

                        user.save(function(err) {
                            if (err)
                                throw err;
                            return done(null, user);
                        });

                    }
                });

            } else {
                var user = req.user;
                user.local.facebook.id = profile.id;
                user.local.facebook.token = accessToken;
                user.local.profile.gender = profile.gender;
                user.local.profile.herobg = profile.cover;
                user.local.profile.dp = user.local.profile.dp ? user.local.profile.dp : profile.photos[0].value;
                if (user.local.profile.dp == '') {
                    if (user.local.profile.gender == 'male') {
                        user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487659283/an-av-3_jxrhwc.png';
                    }
                    if (user.local.profile.gender == 'female') {
                        user.local.profile.dp = 'http://res.cloudinary.com/pinterested222/image/upload/v1487770814/female-avatar_vvyvtj.png';
                    }
                }

                user.save(function(err) {
                    if (err)
                        throw err;
                    return done(null, user);
                });
            }
        });
    }));

Here is my updated routes.js:

app.get('/auth/connect/facebook', passport.authorize('facebook', { authType: 'rerequest', scope: ['id', 'cover', 'gender', 'photos'] }));

app.get('/auth/connect/facebook/callback',
    passport.authorize('facebook', {
        successRedirect: '/profile/configure',
        failureRedirect: '/profile/configure'
            // failureFlash: true
    })
);

Here is the snapshot of my app callback settings from Facebook: enter image description here

Snapshot of the error, facebook keeps throwing in: enter image description here

Upvotes: 2

Views: 2311

Answers (2)

Animesh Singh
Animesh Singh

Reputation: 9322

Taking inspiration from @anton-novik, I fixed the bug.

The problem was in my routes.js file. First have a look at my routes.js file above, and then follow the code below:

app.get('/auth/connect/facebook', ensureLoggedIn('/login'), passport.authorize('facebook', { authType: 'rerequest' }));

app.get('/auth/connect/facebook/callback',
    passport.authenticate('facebook', {
        successRedirect: '/profile',
        failureRedirect: '/profile/settings',
        failureFlash: true
    })
);

There was no need of scope for the request I was making was already approved by Facebook for every app.

And then updated my passport.js file to look like this:

// // Facebook Strategy

passport.use(new FacebookStrategy({
        clientID: configAuth.facebookAuth.clientID,
        clientSecret: configAuth.facebookAuth.clientSecret,
        profileFields: ['id', 'picture.type(large)', 'gender', 'cover'],
        callbackURL: configAuth.facebookAuth.callbackURL,
        passReqToCallback: true
    },
    function(req, accessToken, refreshToken, profile, done) {
        process.nextTick(function() {
            // User is not logged in yet
            if (!req.user) {
                User.findOne({ 'local.facebook.id': profile.id }, function(err, user) {
                    if (err)
                        return done(err);
                    if (user) {
                        if (!user.facebook.token) {
                            user.facebook.token = accessToken;
                            user.facebook.name = profile.displayName;
                            user.facebook.email = profile.emails[0].value;
                            user.save(function(err) {
                                if (err) throw err;
                                return done(null, user);
                            });
                        }
                        return done(null, user);
                    } else {
                        // User should be created here
                        //  and saved to mongoose
                    }
                });
            }
            //else user is logged in and needs to be merged
            else {
                console.log(profile); //display the returned json from fb
                // Connect the user and save the details, since the user already exsists
                var user = req.user;
                user.local.facebook.id = profile.id;
                user.local.facebook.token = accessToken;
                user.local.profile.gender = profile.gender;
                user.local.profile.dp = profile.photos[0].value;
                user.local.profile.herobg = profile._json.cover.source;

                user.save(function(err) {
                    if (err)
                        throw err;
                    return done(null, user);
                });
            }
        });
    }));

Hope, it may help someone. :)

Upvotes: 0

Anton Novik
Anton Novik

Reputation: 1837

Passport.js documentation said:

Values for the scope option are provider-specific. Consult the provider's documentation for details regarding supported scopes.

If you check allowed permissions in Facebook documentation, you will not find such permissions as 'id', 'cover', 'gender', 'photos'. These items are part of a person's public profile.

So, you should change scope in routes.js from:

scope: ['id', 'cover', 'gender', 'photos']

to:

scope: ['public_profile']

or don't specify scope, because public_profile is default facebook permission.

P.S. I told about your "update 1" code version.

Upvotes: 4

Related Questions