Search code examples
node.jspassport.jssession-cookiesauth0

How to fix isAuthenticated=false and no req.user data after successful login


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:

I have looked through many threads on similar issues and tried pretty much everything I can find, but serializeUser() is still not being called. I've tried cookie.secured = true and false, and triple-checked the order of calls (but maybe I missed something?).

index.js:

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

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

// Load environment variables from .env
//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

Solution

  • 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' 
                }); 
    
        });