Save accessToken to local storage using passport-facebook - node.js

I'm new to MEAN Stack, I'm having trouble saving passport-facebook accessToken to localStorage. How do I do this? Below is my setup.
passport.use(new FacebookStrategy({
clientID: passport_config.facebook.clientID,
clientSecret: passport_config.facebook.clientSecret,
callbackURL: passport_config.facebook.callbackURL
},
function(accessToken, refreshToken, profile, done) {
FBAccount.findOne({fbId : profile.id}, function(err, oldUser){
if(oldUser){
done(null,oldUser);
}else{
var newUser = new FBAccount({
fbId : profile.id ,
name : profile.displayName
}).save(function(err,newUser){
if(err) throw err;
console.log(newUser);
done(null, newUser);
});
}
});
}
));

Try this
var localStorage = require('localStorage')
localStorage.setItem('accessToken', accessToken);
FBAccount.findOne({ ....

You can also add token in the cookies by some middleware like
passport.use(new FacebookStrategy({
clientID: '566950043453498',
clientSecret: '555022a61da40afc8ead59c6c26306ed',
callbackURL: 'http://www.localhost:3000/auth/facebook/callback'
}, function(accessToken, refreshToken, profile, done) {
console.log("hello " + profile.displayName);
done(null);
}
));
//Authentication
app.get('/auth/facebook', passport.authenticate('facebook'));
router.get('/auth/facebook/callback', passport.authenticate('facebook', {
failureRedirect: '/login?failedSocial=facebook'
}), auth.authCallback);
and in auth service
exports.authCallback = function (req, res) {
res.res.cookie('token', JSON.stringify(req.user.token));
res.redirect('/');
}

Related

passportJS: req.user is returning undefined

i have simple passport-facebook & google oauth application and it works but problem it is not serializing users properly i guess because when i log req.user it returns undefined. here is my code for facebook oauth
passport.serializeUser((user,done)=>{
done(null,user.id)
})
passport.deserializeUser((id,done)=>{
const user = User.findById(id)
done(null, user)
})
passport.use(new FacebookStrategy({
clientID: process.env.FB_CLIENT_ID,
clientSecret: process.env.FB_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
async (accessToken, refreshToken, profile, done) => {
const user = await User.findOne({ userId: profile.id })
if(user){
console.log('existing user '+ user)
return done(null,user)
}else{
const newuser = User.create({ username: profile.displayName,userId: profile.id });
console.log(newuser.toString())
done(null,newuser)
}
}))
and here is passport-google-oauth2
passport.serializeUser((user,done)=>{
done(null,user.id)
})
passport.deserializeUser((id,done)=>{
const user = User.findById(id)
done(null, user)
})
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback"
},
async (accessToken, refreshToken, profile, done) => {
const user =await User.findOne({ userId: profile.id })
if(user){
console.log('existing user '+ user )
return done(null,user)
}else{
const newuser = User.create({ username: profile.displayName,userId:
profile.id });
console.log(newuser)
done(null,newuser)
}
}))
and here is route config
router.get('/facebook', passportFacebook.authenticate('facebook'));
router.get('/facebook/callback',passportFacebook.authenticate('facebook', { failureRedirect: '/auth/login' }),
function(req, res) {
res.redirect('/');
});
router.get('/google',
passportGoogle.authenticate('google', { scope: 'profile' }));
router.get('/google/callback',passportGoogle.authenticate('google', {
failureRedirect: '/auth/login' }),
function(req, res) {
res.redirect('/');
});
so what is wrong with this code?
The problem is you don't await for user in deserializeUser.
const user = User.findById(id)
You need to use then or await result from User.findById. So your deserializeUser code should be
passport.deserializeUser(async (id,done) => {
const user = await User.findById(id)
done(null, user)
})

deserializeUser function never called with Google Strategy in passportJS

I'm trying to setup google auth with PassportJS, but my deserializeUser function is never called.
This is my passport.js config:
...
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(sessionUser, done) {
User.findById(sessionUser, function(err, user) {
done(err, user);
});
});
passport.use('google', new GoogleStrategy({
clientID : process.env.GOOGLE_CLIENTID,
clientSecret : process.env.GOOGLE_CLIENTSECRET,
callbackURL : process.env.GOOGLE_CALLBACKURL,
passReqToCallback: true,
},
function(req, token, refreshToken, profile, done) {
process.nextTick(function() {
// console.log(profile);
var values = {
where: { google_id: profile.id },
defaults: {google_id: profile.id, name: profile.displayName}
};
User.findOrCreate(values)
.spread(function(user, created) {
return done(null,user);
});
});
}
));
And these are my routes:
app.get('/auth/google',
passport.authenticate('google', { scope : ['profile', 'email'] }));
// the callback after google has authenticated the user
app.get('/auth/google/callback',
passport.authenticate('google', {
successRedirect : '/portfolio/crypto',
failureRedirect : '/'
}));
And since deserializeUser is never called, req.user is never defined. How can I fix this?

Getting new GitHub access token on every login

I'm using passport-github for authenticating with GitHub, but every time a user logs in, the access token updates. Is that intended behaviour?
Strategy configuration and Express routing:
passport.use(new GithubStrategy({
clientID: process.env.IMPAKT_ID,
clientSecret: process.env.IMPAKT_SECRET,
callbackURL: process.env.IMPAKT_CALLBACK
}, function(accessToken, refreshToken, profile, cb) {
User.findOneAndUpdate({id: profile.id}, {accessToken, username: profile.username}, {upsert: true, new: true}, function(err, user) { // Update code, since access token changes.
return cb(err, user);
});
}
));
app.get("/auth", passport.authenticate('github', {scope: ['public_repo']}));
app.get("/auth/callback", passport.authenticate('github', { failureRedirect: '/auth' }), function(req, res) {
res.redirect('/');
});

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