Passport-facebook access req object from within callback function - node.js

On the callback from Facebook for nodejs passport authentication, how do you get the req object within the callback?
passport.use(new FacebookStrategy({
clientID: 123456789,
clientSecret: 'SECRET',
callbackURL: "http://example.com/login/facebook/callback"
},
function(accessToken, refreshToken, profile, done){
// Is there any way to get the req object in here?
}
));

Setting the passReqToCallback option, as so:
passport.use(new LocalStrategy({ passReqToCallback: true },
function(req, username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (!user.verifyPassword(password)) {
req.flash('error', 'Your password is too long');
req.flash('error', 'Also, it is too short!!!');
return done(null, false);
}
return done(null, user);
});
}
));
req becomes the first argument to the verify callback
As per https://github.com/jaredhanson/passport/issues/39

I am answering it too late, but i think my solution is better and more conventional.
In the official documentation here. There is a section "Association in Verify Callback", in which it is mentioned that if we set the strategy's passReqToCallback option to true, this enables req and it will be passed as the first argument to the verify callback.
So my FacebookStrategy now looks like:
var User = require('../models/UserModel.js');
var FacebookStrategy = require('passport-facebook').Strategy;
exports.facebookStrategy = new FacebookStrategy({
clientID: 'REPLACE_IT_WITH_CLIENT_ID',
clientSecret: 'REPLACE_IT_WITH_CLIENT_SECRET',
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);
});
});
}
});
}
);
In my code sample i have added some logic to save user info in DB and saving user details in session. I thought it might be helpful to people.
req.user gives the information of user stored in passport session.

Related

how to return an error after login failed in passport oauth

I have a node js server, as a login strategy I use passport with google OAuth.
this is my code
passport.use(new GoogleStrategy({
clientID: ...,
clientSecret: ...,
callbackURL: "http://localhost:3000/users/googleAuth/callback"
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function(){
UserDB.findOne({'google.id': profile.id}, function(err, user){
if(err)
return done(err);
if(user)
if(user.active)
return done(null, user);
else
return done("user was disabled");
else {
var newUser = new UserDB();
newUser.admin = false;
newUser.email = profile.emails[0].value;
newUser.google.id = profile.id;
newUser.google.token = accessToken;
newUser.google.name = profile.displayName;
newUser.google.email = profile.emails[0].value;
newUser.save(function(err){
if(err)
console.log(err);
else
return done(null, newUser);
});
console.log(profile);
}
});
});
}
));
and this is the callback
router.get('/googleAuth/callback',
passport.authenticate('google', {successRedirect:'/'}), function (req,res) {
if(!req.isAuthenticated())
res.redirect('/');
});
the user have an active property ,if it set to false the user can't log in (you can see this in the code) , when this happen I want to redirect the user to the main page with an error message, I tried to use res.redirect('/'), however it didn't work ,the user is just stack in the screen where you pick a google user ,I also tried to add failureRedirect property but with the same results.
How can I redirect the user? is it also possible to send a message?

passport js with GET method

I'm trying to confirm users email addresses by sending an activator code to their email address. when user clicked on activator URL (GET method), server will compare activator code + username and try to handle it.
I'm using GET method like this:
router.get('/activator/:username/:activator', function(req, res, next){
passport.authenticate('activator', function(err, user, info){
if (err) {console.log('Error info: ', info);}
else if (!user) {console.log('User not found: ' , info)}
else {console.log('User activated')}
res.redirect('/');
})(req, res, next)
});
And activator.js is:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
module.exports = function(passport){
passport.use('activator', new LocalStrategy({
usernameField: 'username',
passwordField: 'activator'
},
function(username, password, done) {
User.findOne({username: username , activator: password},
function(err, user){
if (err){
return done(null, false, 'User not found.');
}
user.activate = true;
user.save(function (err) {
if (err) return handleError(err);
return done(null, user, 'Persistence Registration successful');
});
});
})
);
};
But server response is: { message: 'Missing credentials'}
It seems passport js and GET method params has some conflicts.
Am i right? what should i do for that?
Problem solved!
As I said, problem was for GET method & passport connection.
passport function only read from req.query which is only on POST method but GET method is using req.params.
So I changed my application code to :
router.get('/activator/:username/:activator', function(req, res, next){
req.query = req.params; // GET to POST simulator!
passport.authenticate('activator', function(err, user, info){
if (err) {console.log('Error info: ', info);}
else if (!user) {console.log('User not found: ' , info)}
else {console.log('User activated')}
res.redirect('/');
})(req, res, next)
});
your Strategy fields are not valid, replace it with email and password and send same parameters from client side
module.exports = function(passport) {
passport.use('activator', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
},
function(username, password, done) {
User.findOne({
username: username,
activator: password
},
function(err, user) {
if (err) {
return done(null, false, 'User not found.');
}
user.activate = true;
user.save(function(err) {
if (err) return handleError(err);
return done(null, user, 'Persistence Registration successful');
});
});
}));
};

Specific user info from facebook using passport-facebook

I am using passport-facebook for OAuth and get user information from facebook. But all I am getting is some basic information (i.e. firstname, familyname). I want information like userEmail and friendList.
My current FacebookStrategy is -
passport.use(new FacebookStrategy({
clientID: configAuth.facebookAuth.appID,
clientSecret: configAuth.facebookAuth.appSecret,
callbackURL: configAuth.facebookAuth.callbackUrl
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function() {
User.findOne({'facebook.id': profile.id}, function(err, user) {
if(err)
return done(err);
if(user)
return done(null, user);
else {
var newUser = new User;
console.log(profile);
newUser.facebook.id = profile.id;
newUser.facebook.token = accessToken;
newUser.facebook.name = profile. displayName;
newUser.save(function(err) {
if(err)
throw err;
return done(null, newUser);
})
}
})
})
}
));
Can someone tell me how can I get user's email or friend-list.
Thanks in advance!!
If you need email and user_friends you will have to add them to the scope while requesting for facebook-auth. I guess adding following code to yowill help you:
app.get('/auth/facebook', passport.authenticate('facebook', { scope : ['email,user_friends'] }));

How to differentiate Login from Sign up when using PassportJS for Facebook

I am trying to set up goals on Google Analytics to track Sign Ups, so I set up a 'thank you ' page as my url goal. It works well when my users sign up with their email address but not when they use facebook to sign up/login. When they login, they are redirected to the thank you page as there is only one url callback when setting up Facebook using Passport JS and Node.
Here is my code:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(id, done) {
UserActivity.findOne(id,'uid ref', function (err, user) {
done(err, user);
});
});
passport.use(new FacebookStrategy({
clientID: 'XXXXXXXXXXXX',
clientSecret: 'XXXXXXXXXXXXXXXX',
callbackURL: "https://www.xxxxxxx.com/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
//User exists: we are done
if(uInfo){
done(err, uInfo);
}
else{
//User doesn't exist: we create a new one
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',
passport.authenticate('facebook', { successRedirect: '/thankyou',
failureRedirect: '/login' }));
If the user exists, I would like to redirect to their dashboard ('/dashboard) and if they are new users, I need to redirect them to /thankyou.
Any idea how to achieve this?
Thanks a lot!
Nevermind, found the answer. Here is the updated code below. Pay attention to the use of passReqToCallback and req.session.newu
passport.use(new FacebookStrategy(
{
clientID: 'XXX',
clientSecret: 'XXX',
callbackURL: "https://www.XXX.co/auth/facebook/callback",
passReqToCallback: true
},
function(req, accessToken, refreshToken, profile, done) {
//console.log(profile);
User.findOne({ uid: profile.id }, function(err, uInfo) {
if(err) console.log('Error: '+err);
else{
if(uInfo){
done(err, uInfo);
}
else{
var newUser = new User ({
uid: profile.id,
email:profile.emails[0].value,
ref: 'Facebook'
});
// Saving it to the database.
newUser.save(function (err,uInfo) {
if (err) console.log ('Error on save!');
req.session.newu=true;
done(err, uInfo);
});
}
}
})
}
));
app.get('/auth/facebook', passport.authenticate('facebook',{ scope: 'email' }));
app.get('/auth/facebook/callback',function(req, res, next) {
passport.authenticate('facebook', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
var redLink = '/dashboard';
if(req.session.newu)redLink = '/dashboard?newu'
return res.redirect(redLink);
});
})(req, res, next);
});
An existing user will be redirected to /dashboard and a new user will be redirected to /dashboard?newu
Google Analytics doesn't need 2 different urls, it just needs a query string. When I set up the url goal, I selected url start with /dashboard?newu.
Hope this helps

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.
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.
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.

Resources