Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom Redirects in Passport

I am trying to set up a basic authentication for my first public facing node app. I have two passport strategies: 1) for Facebook and 2) For Twitter. I do not plan to include an email/password system at least for now till I understand the security implications. I have been able to get them working in an out of the box way and tie in Mongoose for creating new users.

I now want to work on deduping social account users. So whenever a new user comes in via twitter auth I want to redirect them to a page that collects email. I am saving the token and profile object returned as session variables and would reuse them on that page when submitting the form as hidden fields.

However I cannot understand how to implement this aspect using Passport. Here is the code I have so far with comments on what I am trying to do with each part. Essentially I am checking if the twitter user is an old user, if not I am setting session variables that I will reuse on the /addemail page, initializing the user object (so that Serialize, Deserialize function has something to act (not sure I understand what Serializing/Deserializing actually does). Now if the user is a new one, the session variable NewTwitterUser is true and I check it in the auth/callback url to redirect the user to appropriate page. This however does not work.

//basic modules and setup
var express = require('express')
    , passport = require('passport')
    , mongoose = require('mongoose')
    , http = require('http')
    , util = require('util')
    , TwitterStrategy = require('passport-twitter').Strategy
    , FacebookStrategy = require('passport-facebook').Strategy
    , path = require('path');

var app = express();


//Mongodb setup
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

var UserSchema = new Schema({
  provider: String,
  uid: String,
  fb_uid: String,
  twitter_uid: String,
  name: String,
  first_name: String,
  gender: String,
  fb_username: String,
  twitter_username: String,
  profile_pic: String,
  email: String,
  location: String,
  birthday: String,
  created: {type: Date, default: Date.now}
});


var User = mongoose.model('User', UserSchema);
mongoose.connect('MongoHQ db connection here')'


//User Authentication - Twitter
passport.use(new TwitterStrategy({
    consumerKey: 'KEY',
    consumerSecret : 'SECRET',
    callbackURL: "CALLBACKURL",
    passReqToCallback: true
},
    function(req, token, tokenSecret, profile, done){
        User.findOne({twitter_uid: profile.id}, function(err, user){
            if (err) {
            console.log('this is an error 1' + err);
            return done(err);}
            if(user){
                console.log('this user' + user);
                done(null, user);
            } else {
                console.log('this is a new user');
                req.session.token = token;
                req.session.tokenSecret = tokenSecret;
                req.session.profile = profile;
                req.session.newtwitteruser = true;
                var user = new User();
                user.uid = profile.id;
                done(null, user);

                /* This part is commented and is the default code I had if I needed to simply create a Twitter User right here.
                var user = new User();
                user.provider = profile.provider;
                user.uid = profile.id;
                user.twitter_uid = profile.id;
                user.name = profile.displayName;
                user.first_name = profile.displayName[0];
                user.twitter_username = profile._json.screen_name;
                user.profile_pic = profile._json.profile_image_url;
                user.location = profile._json.location;
                user.save(function(err){
                    if(err) {throw err;}
                    else {done(null, user);}
                });*/
            }
        }); 
    }   
));


//User Authentication - Facebook
passport.use(new FacebookStrategy({
    clientID: 'ID',
    clientSecret: 'SECRET',
    callbackURL: "URL"
},
    function(accessToken, refreshToken, profile, done){
        User.findOne({fb_uid: profile.id}, function(err, user){
            if (err) {return done(err);}
            if(user){
                done(null, user);
            } else {
                var user = new User();
                user.provider = profile.provider;
                user.uid = profile.id;
                user.fb_uid = profile.id;
                user.name = profile.displayName;
                user.first_name = profile._json.first_name;
                user.gender = profile._json.gender;
                user.fb_username = profile._json.username;
                user.profile_pic = 'https://graph.facebook.com/' + profile.id + '/picture';
                user.email = profile._json.email;
                user.location = profile._json.location.name;
                user.birthday = profile._json.birthday;
                user.save(function(err){
                    if(err) {throw err;}
                    else {done(null, user);}
                });
            }
        })  
    }   
));


passport.serializeUser(function(user, done) {
  done(null, user.uid);
});

passport.deserializeUser(function(uid, done) {
  User.findOne({uid: uid}, function (err, user) {
    done(err, user);
  });
});  


//app configurations
app.configure(function(){
  app.set('port', process.env.PORT || 3000);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser("freecookie"));
  app.use(express.session({secret:"freecookie"}));
  app.use(express.static(path.join(__dirname, 'public')));
  app.use(express.errorHandler());
  app.use(passport.initialize());
  app.use(passport.session());
  app.use(app.router);
});


//Basic Routing
app.get('/', function(req, res){
    res.render('home', {title: 'App Title', user: req.user});
});


app.get('/auth/twitter', passport.authenticate('twitter'));

app.get('/auth/twitter/callback', 
    passport.authenticate('twitter', {failureRedirect: '/login' }), 
    function(req, res) {
        if (req.session.newtwitteruser){
        res.redirect('/addemail');}
        else {res.redirect('/');}
    });

app.get('/addemail', function(req, res){
    if (req.session.newtwitteruser){
    res.render('email', {title: 'Add your Email'});}
    else {res.redirect('/');}
});


app.get('/auth/facebook', passport.authenticate('facebook', {scope: ['email', 'user_location', 'user_birthday'] }));

app.get('/auth/facebook/callback', 
  passport.authenticate('facebook', { successRedirect: '/', failureRedirect: '/login' }));  


app.get('/logout', function(req, res){
  req.logout();
  res.redirect('/');
});



//create the server
var server = http.createServer(app);
server.listen(app.get('port'));


//Checks if a request is authenticated
function ensureAuthenticated(req, res, next) {
  if (req.isAuthenticated()) { return next(); }
  res.redirect('/login')
}
like image 272
Tuhin Kumar Avatar asked Nov 03 '12 04:11

Tuhin Kumar


1 Answers

I would first redirect successful logins to your addemail function inside your twitter route:

  app.get('/auth/twitter', passport.authenticate('twitter'));
  app.get('/auth/twitter/callback',
    passport.authenticate('twitter', { successRedirect: '/addemail',
                                       failureRedirect: '/' }));

Then, inside your addemail function, I would check to see if they already have an email or not, after which you can redirect them to the appropriate spot.

  app.get('/addemail', function(req, res){
      if (req.user.email){
           res.render('email', {title: 'Add your Email'});}
      else {res.redirect('/');}
  });
like image 190
Scott Weinert Avatar answered Oct 04 '22 02:10

Scott Weinert