node.jsexpresspassport.jspassport-google-oauth2

Passportjs Google Authentication Failing


I am trying to debug where in my Google OAuth2 setup is failing to even render the OAuth login window to sign in with my credentials. Despite following the documentation, it appears that my code is failing right when passport.authorize('google', { scope : ['profile'] }) is called as the route is triggered and then has an endless loop until it timesout with an error GET http://localhost:3000/auth/google net::ERR_CONNECTION_REFUSED.

I tried to look through my passport setup, but nothing seems to be different between the code examples in the documentation and not sure where the error would come from as nothing is being logged in my terminal for my console logs outside of Google Auth triggered

Here is the PassportJS setup:

var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var GoogleStrategy = require('passport-google-oauth20').Strategy;
var models = require('../app/models/db-index');
var configAuth = require('./auth');
var bcrypt = require('bcrypt-nodejs');

/*====  Passport Configuration  ====*/

// Serialize sessions
passport.serializeUser(function(user, done) {
    console.log("User ID: " + user.userId + " is serializing");
    done(null, user.userId);
});

passport.deserializeUser(function(userId, done) {
    models.User.find({
        where: {
            userId: userId
        }
    }).then(function(user) {
        console.log("User ID: " + user.userId + " is deserializing");
        done(null, user);
    }).error(function(err) {
        done(err, null);
    });
});

/*====  Local Configuration  ====*/

//Login logic
passport.use('local', new LocalStrategy({
    passReqToCallback: true,
    usernameField: 'email'
}, function(req, email, password, done) {
    //Find user by email
    models.User.findOne({
        where: {
            email: req.body.email
        }
    }).then(function(user) {
        if (!user) {
            done(null, false, {
                message: 'The email you entered is incorrect'
            }, console.log("Unknown User"));
        } else if (!user.validPassword(password)) {
            done(null, false, console.log("Incorrect Password"));
        } else {
            console.log("User match");
            done(null, user);
        }
    }).catch(function(err) {
        console.log("Server Error");
        return done(null, false);
    });
}));

//Sign Up Logic
passport.use('local-signup', new LocalStrategy({
    passReqToCallback: true,
    usernameField: 'email'
}, function(req, email, password, done) {
    models.User.findOne({
        where: {
            email: email
        }
    }).then(function(existingUser) {
        if (existingUser)
            return done(null, false, req.flash('error', 'Email already exists.'));
        if (req.user && password === confirmPassword) {
            var user = req.user;
            user.firstName = firstName;
            user.lastName = lastName;
            user.email = email;
            user.password = models.User.generateHash(password);
            user.save().catch(function(err) {
                throw err;
            }).then(function() {
                done(null, user, req.flash('error', 'All fields need to be filled in'));
            });
        } else {
            var newUser = models.User.build({
                firstName: req.body.firstName,
                lastName: req.body.lastName,
                email: req.body.email,
                password: models.User.generateHash(password)
            });

            newUser.save().then(function() {
                done(null, newUser);
            }).catch(function(err) {
                done(null, false, console.log(err));
            });
        }
    }).catch(function(e) {
        done(null, false, req.flash('error', 'All fields need to be filled in'), console.log(e.email + e.message));
    })
}));

/*====  Google Configuration  ====*/

passport.use(new GoogleStrategy({
    clientID: 'client-id-hash.apps.googleusercontent.com',
    clientSecret: 'secret-hash',
    callbackURL: 'http://localhost:3000/auth/google/callback'
  }, function(accessToken, refreshToken, profile, cb) {
    console.log('First Google Config fired');
        models.ExternalAccount.findOrCreate({ 
            externalSourceId: profile.id 
        }).then(function(err, externalAccount){
            console.log('Sequelize Google query fired without issues');
            return cb(err, externalAccount)
        });
  }));

module.exports = passport;

Here are my routes:

var express = require('express');
var siteRoutes = express.Router();
var path = require('path');
var passport = require(path.resolve(__dirname, '..', '..', './config/passport.js'));

/*====  /AUTH/GOOGLE  ====*/

siteRoutes.route('/auth/google') 
    .get(function(req, res){
        console.log('Google Auth triggered');
        passport.authorize('google', { scope : ['profile'] })
    });

/*====  /AUTH/GOOGLE/CALLBACK  ====*/

siteRoutes.route('/auth/google/callback') 
    .get(function(req, res){
        passport.authorize('google', {
            successRedirect : '/app',
            failureRedirect : '/login',
            failureFlash: 'Invalid Google credentials.'
        });
    });

Solution

  • The authenticate call is itself a middleware. And as far as I can tell from the documentation, there is no authorize call at all. Your callback route should therefore look something like this:

    /*====  /AUTH/GOOGLE/CALLBACK  ====*/
    
    siteRoutes.route('/auth/google/callback') 
        .get(passport.authenticate('google', {
                successRedirect : '/app',
                failureRedirect : '/login',
                failureFlash: 'Invalid Google credentials.'
            }));