Trouble authenticating with passport + Node.js + Heroku

I’m setting up authentication and login for a new heroku app using Auth0. I’ve copied all of my code so far from another app which is working fine, but for some reason the new app implementation does not work. The only difference so far is that the new app is using https and a different domain while the old one was http.

Auth0 authentication is working great, and the user data is being passed to passport.authenticate(), which succeeds and calls req.logIn(). But for some reason passport.serializeUser() and passport.deserializeUser() are never being called. In my views, req.user contains only {“isAuthenticated”:false,“matchingUsername”:null,“appUserHasAccessTo:null”,“isReadOnly”:false}.

How can I fix this issue and get the user data sent to my views? Below is most of the relevant code:

index.js:

//Auth0 Stuff**********

var session = require('cookie-session');
var cookieParser = require('cookie-parser');
var cookieSecret = process.env.EXPRESS_SECRET;

//Environment variables set in Heroku
//var dotenv = require('dotenv');
//dotenv.config();

// Load Passport
var passport = require('passport');
var Auth0Strategy = require('passport-auth0');

// Configure Passport to use Auth0
var strategy = new Auth0Strategy(
  {
    domain: process.env.AUTH0_DOMAIN,
    clientID: process.env.AUTH0_CLIENT_ID,
    clientSecret: process.env.AUTH0_CLIENT_SECRET,
    callbackURL:
      process.env.AUTH0_CALLBACK_URL
  },
  function (accessToken, refreshToken, extraParams, profile, done) {
    // accessToken is the token to call Auth0 API (not needed in the most cases)
    // extraParams.id_token has the JSON Web Token
    // profile has all the information from the user
    return done(null, profile);
  }
);



passport.use(strategy);

// You can use this section to keep a smaller payload
passport.serializeUser(function (user, done) {
console.log('~~~~~~~~~~~~~~~~~~serializeUser~~~~~~~~~~~~~~~~~~~~: ', user)
  done(null, user);
});
passport.deserializeUser(function (user, done) {
console.log('~~~~~~~~~~~~~~~~~~serializeUser~~~~~~~~~~~~~~~~~~~~: ', user)
  done(null, user);
});

app.use(cookieParser(cookieSecret));



// config express-session
var sess = {
  secret: cookieSecret,
  resave: false,
  // Cookie Options
  maxAge: 4 * 60 * 60 * 1000, // 4 hours
 // secure: true,                               
  cookie: { secure: true }, 
 // signed: true
 saveUninitialized: false

};
//sess.cookie.sameSite = true; // to help issue with passport.authenticate
sess.cookie.secure = true;
app.set('trust proxy', 1) // trust first proxy 


app.use(session(sess));
app.use(passport.initialize());
app.use(passport.session());

auth.js:

var express = require('express');
var router = express.Router();
var passport = require('passport');

// Perform the login, after login Auth0 will redirect to callback
router.get('/login', passport.authenticate('auth0', {
  scope: 'openid email profile https://*****************/parselogin'
}), function (req, res) {
  res.redirect('/');
});

// Perform the final stage of authentication and redirect to previously requested URL or '/user'
router.get('/callback', function (req, res, next) {
  passport.authenticate('auth0', function (err, user, info) {
    if (err) { return next(err); }
    if (!user) {
        if (info) {
            //alert(info);
            if (info.includes('unauthorized')) { return res.send('<meta http-equiv="refresh" content="10; URL=logout">Sorry, your email address has not been authorized.'); }
        } 
        else {
            return res.send('<meta http-equiv="refresh" content="10; URL=logout">Oops! Something has gone wrong. This is usually caused by stale cookies on your device. Please clear your browser cache (or hit ctrl+refresh), and try again.');
        }
    //console.log('************User is FALSE');
    //  console.log('************' + info.message);
    //return res.send('Status: user is <br>'+ user);        
    //return res.send('<br>Eror Message: <br>' + info);
    return res.redirect('/login'); 
    }
    req.logIn(user, function (err) {
    console.log('************User is:');
    console.log(user);
      if (err) { return next(err); }
      const returnTo = req.session.returnTo;
      delete req.session.returnTo;
      res.redirect(returnTo || '/user2');
    });
  })(req, res, next);
});

secured.js:

module.exports = function () {
  return function secured (req, res, next) {
    console.log('~~~~~~~~~~~~~~~~~~SecuredMiddleware~~~~~~~~~~~~~~~~~~~~: ', req.user);
    if (req.user) { return next(); }
    req.session.returnTo = req.originalUrl;
    res.redirect('/login');
  };
};

console output:

2019-07-30T23:06:28.339962+00:00 heroku[router]: at=info method=GET path="/callb
ack?code=*************&state=*************" host=www.************* request_id=************* fwd="*************" dyn
o=web.1 connect=0ms service=722ms status=302 bytes=2290 protocol=https
2019-07-30T23:06:28.328064+00:00 app[web.1]: ************User is:
2019-07-30T23:06:28.332831+00:00 app[web.1]: Profile {
2019-07-30T23:06:28.332835+00:00 app[web.1]: displayName: '*************',
2019-07-30T23:06:28.332838+00:00 app[web.1]: id: 'facebook|*************',
2019-07-30T23:06:28.332839+00:00 app[web.1]: user_id: 'facebook|*************
{**************rest of full user profile removed******}

2019-07-30T23:06:28.486020+00:00 app[web.1]: ~~~~~~~~~~~~~~~~~~UserInViews~~~~~~
~~~~~~~~~~~~~~:                  { isAuthenticated: false,
2019-07-30T23:06:28.486025+00:00 app[web.1]: matchingUsername: null,
2019-07-30T23:06:28.486027+00:00 app[web.1]: appsUserHasAccessTo: null,
2019-07-30T23:06:28.486029+00:00 app[web.1]: isReadOnly: false }
2019-07-30T23:06:28.486408+00:00 app[web.1]: ~~~~~~~~~~~~~~~~~~SecuredMiddleware
~~~~~~~~~~~~~~~~~~~~:               { isAuthenticated: false,
2019-07-30T23:06:28.486412+00:00 app[web.1]: matchingUsername: null,
2019-07-30T23:06:28.486413+00:00 app[web.1]: appsUserHasAccessTo: null,
2019-07-30T23:06:28.486415+00:00 app[web.1]: isReadOnly: false }
2019-07-30T23:06:28.486692+00:00 app[web.1]: ~~~~~~~~~~~~~~~~~~ROUTER VARS~~~~~~
~~~~~~~~~~~
2019-07-30T23:06:28.486794+00:00 app[web.1]: _raw = undefined
2019-07-30T23:06:28.486880+00:00 app[web.1]: _json = undefined
2019-07-30T23:06:28.486970+00:00 app[web.1]: userProfile = [object Object]
2019-07-30T23:06:28.487076+00:00 app[web.1]: req.user = {"isAuthenticated":false
,"matchingUsername":null,"appsUserHasAccessTo":null,"isReadOnly":false}
2019-07-30T23:06:29.061481+00:00 heroku[router]: at=info method=GET path="/user2
" host=www.*************request_id=e705f132-fa27-4935-b0d9-d84ffef82e30
 fwd="*************" dyno=web.1 connect=0ms service=578ms status=500 bytes=487 p
rotocol=https
2019-07-30T23:06:29.059933+00:00 app[web.1]: TypeError: Cannot read property 'in
cludes' of undefined

100% same issue here. I can run this locally without issue, so either something in the Heroku environment is preventing some of these calls, or something with the Auth0 addon is borked. This hurts, as I’m using this in my capstone project for my degree… due this weekend.

Good to know. Are you using https? Also, do you have your own domain pointing to the app (i.e. www.example.com) or are you using Heroku’s subdomain (i.e. yourapp.herokuapp.com)?
I was thinking possibly the issue could be related to DNS redirection, but that’s hard for me to test.

I’ve been stuck on this for two weeks now. Bummer.

Erik… Can you post your login screen here? I just found a major oversight on my part…

Not sure what you mean. In my app, when a user goes to /login, it immediately brings up the Auth0 login screen. auth0 handles that and then redirects the logged-in user back to my site.

So I was never able to figure out what was causing this problem. Passport just will not stay logged in for some reason.

The best I could do was build a workaround. Now inside req.logIn(), instead of redirecting the user to a page on success, I’m just rendering the page directly. This allows use of the user data sent from Auth0, before it is lost. Then I’m using that data client-side to log the user in using my local Parse server, and storing the user data in my local Parse database for later use. Not ideal, but it works.

    req.logIn(user, function (err) {
    const { _raw, _json, ...userProfile } = user;

            if (err) { return next(err); }

            res.locals.user = user;
            res.render('addplace', { 
            userProfile: JSON.stringify(userProfile, null, 2),
            path: req.originalUrl,
            title: 'Add Place' 
            }); 

    });

If you use the customized domain, I think my experience would help you.

I also had the same issue and figured out that my customized domain is proxied from Cloudflare to heroku. I think that’s why the callback response loses along the way.
The easiest solution would be just going for heroku hobby plan or using the default heroku domain.

1 Like

Thanks for sharing that with the rest of community!

1 Like