Retrieving photo from Facebook using passport-facebook - node.js

I am able to retrieve basic user information via passport-facebook, following the below code and saving in mongodb:
app.get("/auth/facebook", passport.authenticate("facebook", { scope : ["email", "publish_stream", "user_location", "user_hometown", "user_birthday", "read_friendlists"]}));
app.get("/auth/facebook/callback", passport.authenticate("facebook",{ successRedirect: '/', failureRedirect: '/'}));
var mongoose = require('mongoose'),
FacebookStrategy = require('passport-facebook').Strategy,
Users = mongoose.model('Users');
module.exports = function (passport, config) {
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
Users.findOne({ _id: id }, function (err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL
}, function(accessToken, refreshToken, profile, done) {
Users.findOrCreateFaceBookUser(profile, done);
}));};
However, I am not able to see the profile picture in the "profile".
The documentation https://github.com/jaredhanson/passport-facebook says to retrieve photos we need to pass the profileFields as below. But doing so, I am able to see the photo URL but loosing other data which were contained in _json e.g. profile._json.location.name. How can I retrieve photo with other user information intact?
passport.use(new FacebookStrategy({
// clientID, clientSecret and callbackURL
profileFields: ['id', 'displayName', 'photos', ....]},// verify callback));

If you need a larger image (default in miksii's example above is 50px x 50px which is pretty small), then use:
profileFields: ['id', 'displayName', 'name', 'gender', 'picture.type(large)']
and then
picture: profile.photos ? profile.photos[0].value : '/img/faces/unknown-user-pic.jpg'
This will return a 200px x 200px profile picture for that user.

In addition to answer of your question - you don't have to do it that way. As you mentioned you can define the required attributes for Facebook profile:
clientID: "...",
clientSecret: "...",
callbackURL: "...",
profileFields: ['id', 'displayName', 'name', 'gender', ..., 'photos']
What than you can do is just simply grab the value of the given attribute. Let's say you want to make an attribute that will hold this value:
picture: profile.photos ? profile.photos[0].value : '/img/faces/unknown-user-pic.jpg'
This proved to be a better solution since some users or sometimes the value of username may be undefined.
I hope you find this useful too,
Thank you.

Yes, the picture can be accessed via the graph api using the access token like this. "graph.facebook.com/"; + profile.username + "/picture" + "?width=200&height=200" + "&access_token=" + accessToken; There is no need to use the profile fields.

As this answer, it will be work better with this code.
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: FACEBOOK_APP_CALLBACK,
profileFields: ['id', 'displayName', 'picture.type(large)', 'email', 'birthday', 'friends', 'first_name', 'last_name', 'middle_name', 'gender', 'link']
}, (accessToken, refreshToken, profile, cb) => {
const picture = `https://graph.facebook.com/${profile.id}/picture?width=200&height=200&access_token=${accessToken}`
//
}))

2020 solution
Get users accessToken from passportjs passport-facebook strategy. Use this token to get json with a url for an users avatar:
https://graph.facebook.com/me/picture?access_token=${accessToken}&&redirect=false

Related

Passport Facebook Authentication Returns Undefined?

My facebook strategy is as follows:
passport.use(new facebookStrategy({
clientID: envVars.facebook.clientID,
clientSecret: envVars.facebook.clientSecret,
callbackURL: envVars.facebook.callbackURL,
passReqToCallback : true,
profileFields: ['id', 'emails', 'displayName']
},
function(token, refreshToken, profile, done) {
console.log(profile); //prints out undefined
}
));
And my routes are handled as follows:
router.get('/facebook', passport.authenticate('facebook'));
router.get('/auth/facebook/redirect', passport.authenticate('facebook'),
function(req, res){
res.redirect("/profile");
});
What my code manages to do successfully is direct the user to Facebook where they are prompted with a permission to allow my app to access their data. Once accepted my console.log(profile) fires yet it prints out undefined even though the user accepts the permission? I have searched through the documentation and can't seem to figure out where I went wrong.
Seems to be a problem with passReqToCallback. Refer this SO question: Using PassportJS, how does one pass additional form fields to the local authentication strategy?
In your case, remove the passReqToCallback:true flag.
passport.use(new facebookStrategy({
clientID: envVars.facebook.clientID,
clientSecret: envVars.facebook.clientSecret,
callbackURL: envVars.facebook.callbackURL,
profileFields: ['id', 'emails', 'displayName']
},
The other option is to modify your callback and use the first argument, which is the request object.
function(req, email, password, done) {
// now you can check req.body.foo
}

using passport module to get facebook profile events

i am trying to get future events user attended to using passport module but im clearly missing something. i followed the Facebook API and got the permission i needed like so :
router.get('/auth/facebook', passport.authenticate('facebook', { scope: [ 'public_profile', 'user_events' ] }));
router.get('/auth/facebook/callback', passport.authenticate('facebook', {
successRedirect:'/welcome',
failureRedirect:'/'
}))
but when i am trying to print an event like this:
passport.use(new FacebookStrategy({
clientID: config.fb.appID,
clientSecret: config.fb.appSecret,
callbackURL: config.fb.callbackURL,
profileFields: ['id', 'displayName', 'photos', 'birthday', 'events', 'profileUrl', 'emails', 'likes']
}, function(accessToken, refershToken, profile, done){
//Check if the user exists in our Mongo DB database
//if not, create one and return the profile
//if exists, return profile
userModel.findOne({'profileID':profile.id}, function(err, result){
if(result){
done(null,result);
} else {
// Create a new user in our mongoLab account
var newFbUSer = new userModel({
profileID: profile.id,
fullname: profile.displayName,
profilePic:profile.photos[0].value || '',
birthday:profile.birthday,
events:profile.events[0].name,
profileUrl:profile.profileUrl,
});
console.log(newFbUSer.profilePic);
newFbUSer.save(function(err){
done(null,newFbUSer);
})
}
})
i get this error:
events:profile.events[0].name,
^
TypeError: Cannot read property '0' of undefined
i tried to replace name with value, as it is working perfectly fine with the photos, and i tried many other ways but i simply can't get this one to work...any help please?

PassportJS to get Facebook User Location

I've attempted to set my strategy to get a user's home in two ways, and neither of which work. Does anyone have code for PassportJS to get a user's address (just city and state)?
Strategy attempting both "location" and "address":
profileFields: ['id', 'name','picture.type(large)', 'emails', 'displayName', 'location', 'address', 'about', 'gender'],
Attempting to get using address as seen here portablecontacts.net:
user.facebook.location = profile.address.locality + ', ' profile.address.region;
Edit: Simpler answer
Those location fields not yet mapped in the code from their Portable Contacts schema to Facebook's own schema. You can see that map here (and the absence of those two fields).
Instead, simply use the Facebook schema directly. So the profileFields array should include the location field, then when you call authenticate, you need to request that field from Facebook:
passport.authenticate('facebook', { scope: ['email', 'public_profile', 'user_location'] }));
Note that in the scope array, it is referred to as user_location but in the profileFields array it is just location
It's not working when use it in scope, use in declaration strategy instead
passport.use(new fbStrategy({
clientID: fbClientId,
clientSecret: fbClientSecret,
callbackURL: fbCallback,
profileFields: [
'location', // <- HERE
'email', 'first_name','last_name',
'picture.type(large)', 'link'
]
}, function(accessToken, refreshToken, profile, cb) {
....
})
)

passport-facebook in nodejs - profile fields

some of the profile fields i am trying to get from Facebook when logging in a user, are not going through.
I am using passportjs in node. this is the facebook strategy:
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: FACEBOOK_CALLBACK_URL,
profileFields: ['id', 'displayName', 'link', 'about_me', 'photos', 'email']
},
routes.handleLogin
));
being used with:
app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['user_about_me', 'email'] }));
the result is that 'link', 'about_me' and 'email' are not getting pulled while the other fields are.
The profileFields parameter adheres to the Portable Contacts convention. This means you would want to use 'emails' instead of 'email'. As for the "about_me" field, it does not appear as if passport-facebook supports the OpenSocial protocol fully. This means you're out of luck if you want to use the "profileFields" parameter for both of these profile elements. The following code snippet, taken from the master branch, illustrates this limitation:
Strategy.prototype._convertProfileFields = function(profileFields) {
var map = {
'id': 'id',
'username': 'username',
'displayName': 'name',
'name': ['last_name', 'first_name', 'middle_name'],
'gender': 'gender',
'profileUrl': 'link',
'emails': 'email',
'photos': 'picture'
};
...
The fields listed in this mapping are the only ones supported right now.
Fortunately all is not lost. If you choose not to use the profileFields parameter then oddly enough you will be sent the "about_me" content you are interested in via a property called "bio". Here's how you could access that:
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: FACEBOOK_CALLBACK_URL
},
function(accessToken, refreshToken, profile, done) {
console.log("bio: " + profile._json.bio);
}));
Unfortunately this doesn't give you the other data you were interested in. I'm guessing that in your situation you are probably looking at gathering the supported convention fields during the passport-facebook callback and then grabbing the extended profile fields in a followup call using the facebook api directly. Either that or poke the passport-facebook maintainers to extend their field support.
aturkelson is correct. about_me is not supported yet. As far as email it comes with the profile as long as you request it. I also have a console log to confirm I am not crazy.
//Passport facebook strategy
exports.passportInit= passport.use(new facebookStrategy({
clientID: process.env.FACEBOOK_APP_ID ,
clientSecret: process.env.FACEBOOK_SECRET_ID,
callbackURL: '/api/auth/facebook/callback',
profileFields: ['id', 'displayName', 'emails', 'photos']
},
function(accessToken, refreshToken, profile, done) {
console.log(profile);
db.User.findOne({facebook_id: profile.id}, function(err, oldUser){
if(oldUser){
done(null,oldUser);
}else{
var newUser = new db.User({
facebook_id : profile.id,
facebook_photo : profile.photos[0].value,
email : profile.emails[0].value,
display_name : profile.displayName,
// picture: profile.picture
}).save(function(err,newUser){
if(err) throw err;
done(null, newUser);
});
}
});
}
));
According to my knowledge FB is providing you the info...
try this piece of code..
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: FACEBOOK_CALLBACK_URL,
profileFields: ['id', 'displayName', 'email'] },
function(accessToken, refreshToken, profile, done) {
// asynchronous
process.nextTick(function() {
FACEBOOK_TOKEN = accessToken;
FACEBOOK_USER = profile._json;
// facebook can return multiple emails so we'll take the first
profile.emails[0].value;
console.log(FACEBOOK_USER.email);
done(null, profile);
});
}));

Facebook OAuth2 does not provide user email

This has been asked many times but it seems like there's no known work-around for it so I'm posting this question in the hope that someone does have a work-around for it.
I'm using NodeJS, PassportJS-Facebook.
app.get("/auth/facebook",
passport.authenticate("facebook", {
scope : [ "email" ]
}),
function (req, res) {
});
At first I thought it's a PassportJS issue but I certainly eliminated this option.
The Facebook user account I'm using clearly states:
This app needs:
Your basic info
Your email address (xyz#example.com)
Some links to this known issue (yet unsolved!):
https://developers.facebook.com/bugs/298946933534016
https://developers.facebook.com/bugs/429653750464521
https://developers.facebook.com/bugs/482815835078469
So, do you use Facebook's OAuth service? If so, do you get the user's email? How? The "straight" way? A work-around?
The Facebook strategy in passportjs, expects a profileFields field in the options. Try passing "email" in the options.
strategyOptions.profileFields = ['emails', 'first_name', 'last_name'];
Alternatively, you can override the profileUrl in the options and send:
strategyOptions.profileURL = 'https://graph.facebook.com/me?fields=location,first_name,last_name,middle_name,name,link,username,work,education,gender,timezone,locale,verified,picture,about,address,age_range,bio,birthday,cover,currency,devices,email,favorite_athletes,id,hometown,favorite_teams,inspirational_people,install_type,installed,interested_in,languages,meeting_for,name_format,political,quotes,relationship_status,religion,significant_other,sports,updated_time,website';
Facebook will ignore fields that you don't have a permission to (like email).
This should go here:
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback",
profileUrl: " ..... ",
//or
profileFields: [ ... ];
},
function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Facebook profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Facebook account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}
));
```
You must provide field 'scope' in settings object:
new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback",
profileUrl: " ..... ",
scope: "email",
//or
profileFields: [ ... ];
}
try to look sources.

Resources