I am trying to establish a login system for my app using passport-facebook.
everything goes well except for the 2 fields that are getting undefined back from the request.
I will post my entire code for the login procedure, since I haven't seen a lot of info about it here even though there are a lot of question in the matter.
this is the configuration in app.js
var passport = require('passport');
var FacebookStrategy = require('passport-facebook').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.facebookId);
});
passport.deserializeUser(function(id, done) {
routes.findUserById(id, function(err, user) {
done(err, user);
});
});
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
));
using passport initialize and session
app.use(passport.initialize());
app.use(passport.session());
actual request handling, notice I am using the correct scope
app.get('/auth/facebook', passport.authenticate('facebook', { scope: ['user_about_me', 'email'] }));
app.get('/auth/facebook/callback', passport.authenticate('facebook', { successRedirect: '/', failureRedirect: '/error' }));
and this is my user creation function in the router
exports.handleLogin = function(accessToken, refreshToken, profile, done) {
db.userCatalog.findOne({ facebookId: profile.id }, function(err, existingUser) {
if (err) {
return done(err);
}
else {
if(existingUser) {
console.log('User: ' + existingUser.name + ' found and logged in!');
done(null, existingUser);
}
else {
new db.userCatalog({
name: profile.displayName,
facebookId: profile.id,
link: profile.link,
picture: profile.photos[0].value,
bio: profile.about_me,
email: profile.email
}).save(function (err, data) {
if (err) {
return done(err);
}
else {
console.log('New user: ' + data + ' created and logged in!');
done(null, data);
}
});
}
}
});
};
and the result when creating a new user after finishing the login procedure:
I am sure this is some rookie mistake, but I just can't figure it out myself...
Facebook returns some of the default attributes. If you want to access more details about client's profile you would have to declare it under the FacebookStrategy:
passport.use(new FacebookStrategy({
clientID: "...",
clientSecret: "...",
callbackURL: "...",
profileFields: ['id', '...', '...', 'photos', 'emails']
}, ...
So once you declare the attributes you would like to receive from Facebook, when someone try to log into your system he will be asked to share his photos or emails with you/your app. Once he approve this you can access its values:
profile.photos[0].value,
profile.emails[0].value,
...
For emails, sometimes it is useful to change:
passport.authenticate('facebook');
To this:
passport.authenticate('facebook', { scope: 'email'}));
In the profileFields you shoul use emails (plular) instead of email (singular):
profileFields: ['id', 'displayName', 'link', 'about_me', 'photos', 'emails']
This is noted in the facebook-passport documentation README.md:
profileFields parameter which specifies a list of fields (named by Portable Contacts convention)
And you can find Portable Contacts conventions for passportjs here
Related
Unable to retrieve the user email on LinkedIn. I have used passport-LinkedIn and OAuth 2.0. I can interpolate the username and picture. This is the code that I have tried.
var LinkedIn = require('passport-linkedin-oauth2').Strategy;
module.exports = (passport, User) => {
passport.use( new LinkedIn({
clientID: '86ew637ipvirsa',
clientSecret: 'HoEMfqCBGL9SxsIt',
callbackURL: 'http://localhost:3000/auth/linkedin/callback'
}, (accesstoken, refreshToken, profile, done) => {
User.findOne({'linkedin.id': profile.id}, (err, x) => {
if (x) return done(null, x);
var user = {
displayName: profile.displayName,
image: profile._json.pictureUrl,
email: profile.emailAddress,
linkedin: {
id: profile.id
}
};
User.create(user);
User.create(user, (err, x) => done(null, x));
});
}));
};
the npm package being used by you is not properly documented. The author has not explicitly said how you can access the email field from the profile variable.
You can pass in the scope with strategy and get the email fields by logging the profile variable.
passport.use(new LinkedInStrategy({
clientID: LINKEDIN_KEY,
clientSecret: LINKEDIN_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/linkedin/callback",
scope: ['r_emailaddress', 'r_basicprofile'], //pass the scope
state: true
}, function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
console.log(profile); //logging
return done(null, profile);
});
}));
You can also use another package . Here you can explicitly define the profile fields you want to access.
currently I am working on a project to store information from Facebook Authentication, I already checked Passport-facebook doesn't get email to see the solution, and here is my code
passport.use(new FacebookStrategy(
{
clientID: 'xxxxxxxxxxxxx',
clientSecret: 'xxxxxxxxxxxxxxxx',
callbackURL: 'http://1bc600b4.ngrok.io/auth/facebook/callback',
profileFields: ['id', 'displayName', 'name', 'photos', 'email'],
},
(accessToken, refreshToken, profile, done) => {
console.log(profile);
models.User.findOne({ where: { facebookID: profile.id } }).then((existinguser) => {
if (existinguser) {
// Nothing will happen, the ID already exists
done(null, existinguser);
} else {
models.User.create({
// email: profile.emails[0].value,
username: 'hello',
firstname: profile.name.givenName,
lastname: profile.name.familyName,
facebookID: profile.id,
avatar: `https://graph.facebook.com/${profile.id}/picture?redirect=true&height=470&width=470`,
avatarThumb: profile.photos[0].value,
last_login: Date.now(), }).then(user => done(null, user));
}
});
},
));
app.use(passport.initialize());
app.get('/flogin', passport.authenticate(
'facebook',
passport.authenticate('facebook', { scope: ['profile', 'email'] }),
));
app.get(
'/auth/facebook/callback',
passport.authenticate('facebook', { session: false }),
(req, res) => {
res.send('AUTH WAS GOOD!');
},
);
I don't understand why I use this way and the email information still not show up, which makes me lose a lot of information, can anyone give me a hint on solving this prob? Thank you!
This problem has been there for a while, you need to include email scope and even then it might not send facebook email because of user privacy settings or if the user's email is not verified at the facebook's end.
You could add scope like this:
passport.use(
new FacebookStrategy({
clientID: 'CLIENT_ID',
clientSecret: 'CLIENT_SECRET',
callbackURL: "http://www.example.com/auth/facebook/callback"
passReqToCallback : true,
profileFields: ['emails'] // email should be in the scope.
})
)
Also you would need to add this to your authorize route as well,
app.get('/login/facebook', passport.authorize('facebook', { scope : ['email'] }));
If the emails return, It will be an list of it and you could access them like this:
1. profile.emails // [email1#example.com, somerandomdude#gmail.com]
2. profile.emails[1].value // randomdude#yahoo.com
If it doesn't return, you can use their username or id and create a facebook email atleast temporarily like this:
username#facebook.com // this exists.
I am trying to use passportjs to login using facebook services. There are a lot of examples online, but none that really explicitly illustrate an implementation with no persistent sessions as I plan to use JWTs to identify my users.
I have tried to this on my own but I keep getting an error response Cannot GET /auth/facebook/callback. See my implementation below.
Routes file
router.get('/facebook', passport.authenticate('facebook',{ scope : ['email'] }));
router.get('/facebook/callback',
passport.authenticate('facebook', {
session: false
}));
Passportjs
passport.use(new FacebookStrategy({
clientID: config.facebookAppId,
clientSecret: config.facebookAppSecret,
callbackURL: config.facebookCallbackURL,
profileFields: ['email']
},
function(accessToken, refreshToken, profile, done) {
//find the user based off of the facebook profile id
User.findOne({oauthID: profile.id}).exec()
//find the user or create a new record
.then(function(user){
if(user) return done(null, user);
var user = new User({
oauthID: profile.id,
oauthProvider: 'facebook',
email: profile.emails[0].value,
name: profile.displayName
});
user.save();
})
//send back error if encountered
.catch(done);
}
));
How do I get the message and display it in my router.post('/auth')?
passport.use(new FacebookTokenStrategy({
clientID: 'HIDDEN',
clientSecret: 'HIDDEN'
}, function(accessToken, refreshToken, profile, done) {
console.log(profile);
var user = {id: profile.id, first_name: profile.name[1], last_name: profile.name[0], email: profile.emails[0], profile_picture: profile.photos[0]};
var error = null;
return done(error, user, {message: "HOW TO RETRIEVE THIS MESSAGE!"});
}
));
I've tried to retrieve this message by saying console.log(req.message) or console.log(req.session.message), I just don't know how to get it. I've also tried console.log(req.session.passport.message)
router.post('/auth', passport.authenticate('facebook-token'), function(req, res){
console.log("Verifying");
console.log("HOW TO LOG THAT MESSAGE HERE?");
if(req.isAuthenticated()){
console.log(req.session.passport.user);
}else{
console.log("NOT");
}
});
I don't think that the third argument is passed in any way if the authentication was successful (which in your case it always is; by default, Passport will return a 401 when authentication was unsuccessful, and your handler wouldn't get called at all).
However, you can add extra properties to req if you configure the strategy to pass it to the verification callback:
passport.use(new FacebookTokenStrategy({
clientID: 'HIDDEN',
clientSecret: 'HIDDEN',
passReqToCallback : true,
}, function(req, accessToken, refreshToken, profile, done) {
req.message = 'Your Message Here';
...
}
}))
And then in your handler, you can access it as req.message as well.
I tried to implement facebook OAUTH for my applications(Web and Mobile) using Sails.js/Node.js and Mongo as my backend. I wrote a dedicated function to access the endpoints for each app cause the webApp needs a callback URL and the mobile doesn't. My code screenshot is available below. But, I noticed the two gives me two different Facebook id. I am confused, shouldn't the facebookId be unique? I am using these two modules
FacebookStrategy = require('passport-facebook').Strategy
FacebookTokenStrategy = require('passport-facebook-token').Strategy
WebApp Strategy
passport.use(new FacebookStrategy({
clientID : facebook.clientID,
clientSecret : facebook.clientSecret,
profileFields : ['id', 'email', 'last_name', 'first_name', 'gender', 'link', 'verified', 'updated_time', 'timezone', 'age_range'],
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
authenticate(req, accessToken, refreshToken, profile, done);
}
));
The authenticate function being pointed at
var authenticate = function (accessToken, refreshToken, profile, done) {
User.findOne({facebookId: profile.id}).then(function (user){
if (user) {
return [user, {accessToken: accessToken, refreshToken: refreshToken}]
} else {
var data = {
facebookId: profile.id,
firstname: profile._json.first_name,
lastname: profile._json.last_name,
email: profile._json.email,
gender: profile._json.gender,
fb_profile_url: profile._json.link,
fb_verified: profile._json.verified,
fb_updated_time: profile._json.updated_time,
phonenumber: profile._json.phone
}
var userQry = User.create(data);
}
return [userQry, {accessToken: accessToken, refreshToken: refreshToken}]
})
.spread(function (user, credentials){
if (user) {
return done(null, [user, credentials]);
} else {
return done(null, null);
}
})
.catch(function (err){
return done(err, null);
});
}