Search code examples
javascriptnode.jspassport.jspassport-facebook

nodejs passport callback not being called


I am very new to both JS and NodeJs/Express. I am writing a proof of concept MVP node/express app that uses passport for authentication (via social logins).

I have written the server and installed all required packages and so far everything (apart from the authentication bit works).

Note: I have already set up my credentials at the various social media company's ends, so this is not the problem.

Here is a snippet of my code:

app.js

const express = require('express');
const expressLayouts = require('express-ejs-layouts');
compression = require('compression'),
shouldCompress = (req, res) => {
    if (req.headers['x-no-compression']) {
      // don't compress responses if this request header is present
      return false;
    }
    // fallback to standard compression
    return compression.filter(req, res);
  };

const app = express();

// EJS
app.use(expressLayouts);
app.set('view engine', 'ejs');

// Parsing related
app.use(express.urlencoded( { extended: false })); //Parse URL-encoded bodies
app.use(express.json()); //Used to parse JSON bodies

app.use(compression({
    filter:shouldCompress,
    threshold: 3
}));

app.use(express.static('public'));
app.disable('x-powered-by');

// Initialize Passport and restore authentication state, if any, from the session.
var passport = require('passport');
app.use(require('express-session')({ secret: 'keyboard cat', resave: false, saveUninitialized: false }));
app.use(passport.initialize());
app.use(passport.session())

// Routes
app.use('/', require('./routes/index'));
app.use('/member', require('./routes/users'));


const PORT = process.env.PORT || 5000;

app.listen(PORT, console.log(`Server started on port: ${PORT}`));

routes/users.js

const express = require('express');
const router = express.Router();

/* GET authentication funcs */
let authentication = require('../controllers/auth.js');

router.get('/subscribe', (req, res) => { res.render('subscribe'); });
router.post('/subscribe', authentication.subscribe);

module.exports = router;

controllers/auth.js

require('dotenv').config();
const model = require("../models");

const passport = require('passport');

const LocalStrategy = require('passport-local').Strategy;
const FacebookStrategy = require('passport-facebook').Strategy;

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


passport.deserializeUser(function(email, done) {
    done(null, email)
})


function validateLead(req, done) {
    mail = req.body.lead_email;
    console.log('validateLead() called!');

    models.Lead.findOne({
        where: {
            email: email
        }
    }).then(/* some logic */).catch();
        } else {
            // email already taken ..
            return done(null, false, {
                message: 'This email address is already subscribed'
            });
        }
    }).catch((err) => {
        console.log('An error occurred!', err);
    });
}

exports.subscribe = function(req, res, next) {
    switch (req.body.source) {
        case 'facebook':
            passport.use(new FacebookStrategy({
                    clientID: process.env.FACEBOOK_APP_ID,
                    clientSecret: process.env.FACEBOOK_APP_SECRET,
                    callbackURL: process.env.FACEBOOK_CALLBACK_URL
                },
                function(accessToken, refreshToken, fbProfile, done) {
                    console.log('FB callback!');
                    profile = {
                        'email': fbProfile.email,
                        'firstName': '',
                        'LastName': '',
                        'leadSource': '',
                        'tags': [],
                    };
                    return validateLead(req, done, profile);
                }
            ));
            break;
        default:
            console.log('Unknown');
      }
}

views/test.eps

<a href='#'><i id='facebook' class='foobar'></i></a>

(simplified) views/layout.eps

   $(document).ready(function(){
      $('i.foobar').click(function(e){
        $.ajax({
          method: "POST",
          url: "/member/subscribe",
          data: {
            "source": $(this).attr('id')
          },
          dataType: "json",
          timeout: 5000 // 5000ms
        }).done(function(data) {
          // is called if request is successful
          console.log(data.fridge);
        }).fail(function(jqXHR, status) {
          // is called if request fails or timeout is reached
          alert('Request could not complete: ' + status);
        });
      });
    });

I have put console.log() messages in controllers/auth.js and I can see that the FB code branch is being reached. However, when log messages in validateLead() and the FB callback function are not being reached.

What is the cause of this, and how do I fix it?


Solution

  • A couple things I can see :

    As far as I could see you didn't configure passportjs. You need to have a configuration file, which for you would be the controllers/auth.js. To configure passport you need to run require('./controllers/auth')(passport); in app.js.

    For passport to be able to ingest that config you need to export them as a function that takes passport e.g. module.exports = passport => {passport.use('facebook')}

    Your config file (in exports.subscribe) is not a format that passport will understand. Follow the Documentation on how to create that config file.


    Passport provides you with authentication middleware, I am pretty sure that you cannot create "wrappers" for them like in controllers/auth.js. To access passport's auth functions you use passport.authenticate('facebook', callback())(req, res, next) in routes/users.js.

    Passport only provides middleware to serialize and deserialize users.


    Your deserialization is not yet set up. You need a call to the db to fetch the user from session store.