Passport strategy for authenticating with LinkedIn using the OAuth 2.0a API return undefined email on save User - node.js

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.

Related

How to use passport.js github strategy to get emails of users instead of null

I successfully got the user details but the email field is null.After some time got to know that I need to fetch it from "https://api.github.com/user/emails".I used axios and also provided accesstoken as header but it is giving me "▼♥��A�#►���\�%♥+=y��5"V↔ubWevW�迧F�¶◄t��☻�%)H�ɢ�k��♥葩$,�7↓�H�↔?��^Z�k�r���:��x�▬♣▬NP������҇�C�v�C▼o.�pK~☺" instead of emails.
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: "/callbackurl"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ githubId: profile.id }, function (err, user){ //I am using mongoose-findorcreate npm
axios.get('https://api.github.com/user/emails', {
headers: {
"Authorization": "Bearer "+ accessToken,
}
}).then((res)=>{
console.log(res.data);
})
return done(err, user);
});
}
));
It's because you have not specified scope inside your strategy so Inside your github Strategy , also include scope key:
scope: ["user:email"],
As:
`new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: "/callbackurl",
scope: ["user:email"]
}`

How to Get user Email Address using OAuth in Node js

I am writing a code to add functionality of logging in with Google. I have written code but when a user log in with google, it only gives me id, name, fullname etc. It does not provide with user email address. Can any one help me to solve this? Following is my code
passport.use(new GoogleStrategy({
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: "http://localhost:8000/auth/google/notepad"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
router.get('/auth/google', passport.authenticate('google',{scope: ['profile']}));
router.get('/auth/google/notepad',
passport.authenticate('google', { failureRedirect: '/' }),
async function(req, res) {
const token = await req.user.generateAuthToken();
res.cookie('authToken', token);
res.redirect('/')
});
You are missing the email scope. It’s a separate scope to their profile.
See the docs too if you want to know more: https://developers.google.com/identity/protocols/oauth2/openid-connect#scope-param

Passport-Facebook giving two different facebook Id

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);
});
}

Get request object in Passport strategy callback

So here is my configuration for passport-facebook strategy:
passport.use(new FacebookStrategy({
clientID: ".....",
clientSecret: ".....",
callbackURL: "http://localhost:1337/register/facebook/callback",
},
facebookVerificationHandler
));
And here is facebookVerificationHandler:
var facebookVerificationHandler = function (accessToken, refreshToken, profile, done) {
process.nextTick(function () {
.......
});
};
Is there a way to access to the request object in facebookVerificationHandler?
Users are registered to my site with a LocalStrategy but then they will be able to add their social accounts and associate those account with their local accounts. When the callback above is called, the user who is currently logged in is already available in req.user so I need to access req to associate the user with the facebook account.
Is this the correct way to implement it or should I consider another way of doing it?
Thanks.
There's a passReqToCallback option, see the bottom of this page for details: http://passportjs.org/guide/authorize/
Try this.
exports.facebookStrategy = new FacebookStrategy({
clientID: '.....',
clientSecret: '...',
callbackURL: 'http://localhost:3000/auth/facebook/callback',
passReqToCallback: true
},function(req,accessToken,refreshToken,profile,done){
User.findOne({
'facebook.id' : profile.id
},function(err,user){
if(err){
done(err);
}
if(user){
req.login(user,function(err){
if(err){
return next(err);
}
return done(null,user);
});
}else{
var newUser = new User();
newUser.facebook.id = profile.id;
newUser.facebook.name = profile.displayName;
newUser.facebook.token = profile.token;
newUser.save(function(err){
if(err){
throw(err);
}
req.login(newUser,function(err){
if(err){
return next(err);
}
return done(null,newUser);
});
});
}
});
}
);
User is a mongoose model, i save the user in DB.
For this reason instead of setting up the strategy when the application starts I usually setup the strategy when there is a request. for instance:
app.get(
'/facebook/login'
,passport_setup_strategy()
,passport.authenticate()
,redirect_home()
);
var isStrategySetup = false;
var passport_setup_strategy = function(){
return function(req, res, next){
if(!isStrategySetup){
passport.use(new FacebookStrategy({
clientID: ".....",
clientSecret: ".....",
callbackURL: "http://localhost:1337/register/facebook/callback",
},
function (accessToken, refreshToken, profile, done) {
process.nextTick(function () {
// here you can access 'req'
.......
});
}
));
isStrategySetup = true;
}
next();
};
}
Using this you will have access to the request in your verification handler.

passport-facebook - cant get about_me and email profile fields

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

Resources