I want to handle the following passport-facebook related operations differently.
For 'sign-up' I want to do/check:
For 'log-in' and 'connect account' I want to perform a series of other checks/operations.
I've looked at the passport documentation for facebook and the passport-facebook module and a bunch of related Stack Overflow questions, but am still struggling with how to implement this.
How can I implement different facebook strategies (via passport) with different callbackURLs and options?
After a good deal of digging, this is what I ended up doing:
Create 3 separate passport strategies.
Each with different names (facebookSignUp vs facebookLogIn vs facebookConnect) and different callbackURL paths (.../sign-up/clbk vs .../log-in/clbk vs .../connect/clbk).
// facebook strategy 1 (sign-up with facebook)
passport.use('facebookSignUp', new FacebookStrategy({
clientID: FACEBOOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: 'http://website/auth/facebook/sign-up/clbk'
},
function(accessToken, refreshToken, profile, clbk) {
return clbk(profile);
}
));
// facebook strategy 2 (log-in with facebook)
...
// facebook strategy 3 (connect facebook to existing account)
...
Create routes for each strategy.
Initial request route & callback route after facebook authentication. So 2 routes per strategy.
var member = require('../member'); // member module controller
// sign-up with facebook
app.route('/auth/facebook/sign-up')
.get(member.facebookSignUp); // passport redirect to facebook for authentication
// sign-up with facebook callback
app.route('/auth/facebook/sign-up/clbk')
.get(
member.facebookSignUpClbk, // parse facebook profile to create new user & check for existing account -> redirect to log-in route
member.checkEmail, // check if email is already used -> throw error 'please log in with email, then connect facebook in settings'
member.checkUrl, // get unique url
member.signUp, // create new user & sign-in
member.email.welcome, // send welcome email
member.facebookRedirectDashboard // redirect to member dashboard
);
// log-in with facebook
app.route('/auth/facebook/log-in')
.get(member.facebookLogIn); // passport redirect to facebook for authentication
// log-in with facebook callback
app.route('/auth/facebook/log-in/clbk')
.get(
member.facebookLogInClbk, // authenticate user and log-in & check if user exists (fb oauth id or email address) -> throw error 'please sign up with facebook or log in with email'
member.lastLogin, // update user's last login field
member.facebookRedirectDashboard // redirect to dashboard
);
// connect facebook profile to existing account
...
// connect facebook profile to existing account clbk
...
Where the actual passport authentication is performed in the following files/functions.
member.facebookSignUp just calls passport.authenticate(), which redirects to facebook.
// member.facebookSignUp
exports.facebookSignUp = function(req, res, next) {
passport.authenticate('facebookSignUp', { // use the 'facebookSignUp' strategy
display: null, // null = let facebook decide (or 'page' (default), 'popup', 'touch', etc)
scope: [
'public_profile', // profile returned by default, but specified here anywhere
'email', // ask for email address
'user_location' // ask for location
]
})(req, res);
};
member.facebookSignUpClbk gets executed after facebook authorizes the user and redirects to the callback route. It is where everything else happens.
// member.facebookSignUpClbk
exports.facebookSignUpClbk = function(req, res, next) {
// parse profile & plug into req.body for new user creation in later fxn
function parseProfile(profile) {
// user doc
req.body = {};
// facebook profile data
req.body.facebook = (profile._json) ? profile._json : {id: profile.id};
// name
...
// email
...
next();
}
// check existing users (mongoose/mongodb)
function checkUser(profile) {
User.findOne({query}, function(err, userDoc) {
if (err) {
return res.redirect(
'http://'+req.headers.host+
'?header=Sign-Up Error!'+
'&message=We had trouble signing you up with Facebook. Please try again'
);
} else if (userDoc) {
// redirect to log-in fxn
return res.redirect('http://website/auth/facebook/log-in');
} else {
parseProfile(profile);
}
});
}
// passport authentication
function passportAuth() {
passport.authenticate('facebookSignUp', function(profile) {
if (!profile || !profile.id) {
return res.redirect(
'http://'+req.headers.host+
'?header=Sign-Up Error!'+
'&message=We had trouble signing you up with Facebook. Please try again or sign-up via email.'
);
} else {
checkUser(profile);
}
})(req, res);
}
// start process
passportAuth();
};
member.facebookLogIn, .facebookLogInClbk, .facebookConnect, & .facebookConnectClbk are set up in a similar way, just different logic in the 'Clbk' functions depending on what I'm trying to do.
Hope this helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With