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.
Related
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.
Hello people I have been looking in several places also here
134 Email key is not returned when trying to authenticate a user
https://github.com/jaredhanson/passport-facebook/issues/134
I'll paste the standard passport Facebook code.
I specified the profile fields as well. The problem is that the request to FB doesn't fetch the email address of the user, which is essential for my application.
// Load the module dependencies
var passport = require('passport'),
url = require('url'),
FacebookStrategy = require('passport-facebook').Strategy,
config = require('../config'),
users = require('../../app/controllers/users.server.controller');
// Create the Facebook strategy configuration method
module.exports = function() {
// Use the Passport's Facebook strategy
passport.use(new FacebookStrategy({
clientID: config.facebook.clientID,
clientSecret: config.facebook.clientSecret,
callbackURL: config.facebook.callbackURL,
profileFields: ["id", "birthday", "email", "first_name", "gender", "last_name"],
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
// Set the user's provider data and include tokens
var providerData = profile._json;
providerData.accessToken = accessToken;
providerData.refreshToken = refreshToken;
console.log(profile);
var email = profile.emails ? profile.emails[0].value : 'karl.something#something.com';
// Create the user OAuth profile
var providerUserProfile = {
firstName: profile.name.givenName,
lastName: profile.name.familyName,
fullName: this.firstName + ' ' + this.lastName,
email: email,
username: profile.username,
provider: 'facebook',
providerId: profile.id,
providerData: providerData
};
// Save the user OAuth profile
users.saveOAuthUserProfile(req, providerUserProfile, done);
}
));
};
Here you can see the console.log(profile) output:
{ id: '894894191844',
username: undefined,
displayName: undefined,
name: { familyName: 'Something', givenName: 'Karl', middleName: undefined },
gender: 'male',
profileUrl: undefined,
provider: 'facebook',
_raw: '{"id":"1197051993654521","first_name":"Karl","gender":"male","last_name":"Something"}',
_json:
{ id: '1197051993654521',
first_name: 'Karl',
gender: 'male',
last_name: 'Koks',
accessToken: 'XYZ',
refreshToken: undefined } }
Any concrete ideas, would like to fix this issue soon :-)
possibly not the only solution but the following worked for me:
from
passport.authenticate('facebook');
to
passport.authenticate('facebook', { scope: 'email'}));
then the field emails will be populated
Answer found here:
passport-facebook - cant get about_me and email profile fields
My config is identical, except in profileFields i use emails (plural) rather than email.
// configure route
app.get('/auth/facebook', passport.authenticate('facebook', {
scope: [ "email" ]
}));
// configure passport
passport.use(facebook(callback));
// configure strategy
export default (callback) => new FacebookStrategy({
clientID: config.auth.facebook.clientID,
clientSecret: config.auth.facebook.clientSecret,
callbackURL: config.auth.facebook.callbackURL,
passReqToCallback: true,
enableProof: true,
profileFields: ['id', 'emails', 'name', 'gender', 'displayName']
}, callback);
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);
});
}
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