Passport.js TokenError After Deployment to Production

北慕城南 提交于 2020-06-29 04:52:06

问题


I have a MERN stack app which uses Passport.js for Facebook/Twitter/Google authentication. In the development environment, everything works well and we are able to authenticate the user and log them into the application.

After confirming the functionality in the development environment, we deployed to Heroku, but the same functionality is not working in production, and Passport.js is failing with a "TokenError" stating that the Domain URL is not included in the app's domains (even though we confirmed it is listed in the app's domains).

The code is listed below along with screenshots confirming that the URL is in the app's domains. You can also see the live behavior of the callback being successfully called from Facebook in this recording - https://thiag0.tinytake.com/tt/MzcxMjIyNV8xMTI5MTA3MA

Any help pointing me in the right direction is greatly appreciated!

Server.js

const express = require('express');
const connectDB = require('./config/db');
const configurePassport = require('./config/passport');
const path = require('path');
const fs = require('fs');
const https = require('https');

const app = express();

// Connect Database
connectDB();

// Init Middleware
app.use(express.json({ extended: false }));

// Passport Middleware
configurePassport(app);

// Define Routes
app.use('/api/users', require('./routes/api/users'));
app.use('/api/auth', require('./routes/api/auth'));
app.use('/api/profile', require('./routes/api/profile'));
app.use('/api/posts', require('./routes/api/posts'));

// Serve static assets in production
if (process.env.NODE_ENV === 'production') {
  // Set static folder
  app.use(express.static('client/build'));

  app.get('*', (req, res) => {
    res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
  });
}

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

if (process.env.NODE_ENV === 'production') {
  app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
} else {
  https
    .createServer(
      {
        key: fs.readFileSync('config/certificate/server.key'),
        cert: fs.readFileSync('config/certificate/server.cert')
      },
      app
    )
    .listen(PORT, () => console.log(`Server started on port ${PORT}`));
}

Passport.js

const passport = require('passport');
const config = require('config');
const session = require('express-session');
const jwt = require('jsonwebtoken');
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
const uuid = require('uuid');

const FacebookStrategy = require('passport-facebook').Strategy;
const TwitterStrategy = require('passport-twitter').Strategy;
const GoogleStrategy = require('passport-google-oauth2').Strategy;

const User = require('../models/User');

const webHost = config.get('webHost');

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

passport.deserializeUser(function(id, done) {
  User.findById(id, function(err, user) {
    done(err, user);
  });
});

const configurePassport = app => {
  app.use(
    session({
      secret: config.get('jwtSecret'),
      resave: false,
      saveUninitialized: true
    })
  );
  app.use(passport.initialize());
  app.use(passport.session());

  passport.use(
    new FacebookStrategy(
      {
        clientID: config.get('facebook_app_id'),
        clientSecret: config.get('facebook_app_secret'),
        callbackURL: config.get('facebook_callback_url'),
        profileFields: ['email', 'name']
      },
      async (accessToken, refreshToken, profile, done) => {
        const { email, last_name, first_name, id } = profile._json;

        let user = await User.findOne({ facebook_id: id });

        if (!user) {
          // Create user if they don't exist
          user = new User({
            ...
          });

          await user.save();
        }

        done(null, user);
      }
    )
  );

  passport.use(
    new TwitterStrategy(
      {
        consumerKey: config.get('twitter_consumer_key'),
        consumerSecret: config.get('twitter_consumer_secret'),
        callbackURL: config.get('twitter_callback_url'),
        userProfileURL:
          'https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true'
      },
      async function(token, tokenSecret, profile, done) {
        const { email, name, id } = profile._json;

        let user = await User.findOne({ twitter_id: id });

        if (!user) {
          // Create user if they don't exist
          user = new User({
            ...
          });

          await user.save();
        }

        done(null, user);
      }
    )
  );

  // Use the GoogleStrategy within Passport.
  //   Strategies in Passport require a `verify` function, which accept
  //   credentials (in this case, an accessToken, refreshToken, and Google
  //   profile), and invoke a callback with a user object.
  passport.use(
    new GoogleStrategy(
      {
        clientID: config.get('google_app_id'),
        clientSecret: config.get('google_app_secret'),
        callbackURL: config.get('google_callback_url')
      },
      async function(accessToken, refreshToken, profile, done) {
        const { email, name, sub } = profile._json;

        let user = await User.findOne({ google_id: sub });

        if (!user) {
          // Create user if they don't exist              
          user = new User({
            ...
          });

          await user.save();
        }

        done(null, user);
      }
    )
  );

  app.get(
    '/auth/facebook/callback',
    passport.authenticate('facebook', {
      session: false,
      failureRedirect: webHost + '/login'
    }),
    function(req, res) {
      const payload = {
        user: {
          id: req.user._id
        }
      };

      jwt.sign(
        payload,
        config.get('jwtSecret'),
        { expiresIn: 36000 },
        (err, token) => {
          if (err) throw err;
          return res.redirect(webHost + '/social/facebook/' + token);
        }
      );
    }
  );

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

  // Redirect the user to Twitter for authentication.  When complete, Twitter
  // will redirect the user back to the application at /auth/twitter/callback
  app.get(
    '/auth/twitter',
    passport.authenticate('twitter', {
      session: false
    })
  );

  // Twitter will redirect the user to this URL after approval.  Finish the
  // authentication process by attempting to obtain an access token.  If
  // access was granted, the user will be logged in.  Otherwise, authentication has failed.
  app.get(
    '/auth/twitter/callback',
    passport.authenticate('twitter', {
      session: false,
      failureRedirect: webHost + '/login'
    }),
    function(req, res) {
      const payload = {
        user: {
          id: req.user._id
        }
      };

      jwt.sign(
        payload,
        config.get('jwtSecret'),
        { expiresIn: 36000 },
        (err, token) => {
          if (err) throw err;
          return res.redirect(webHost + '/social/twitter/' + token);
        }
      );
    }
  );

  // GET /auth/google
  app.get(
    '/auth/google',
    passport.authenticate('google', {
      session: false,
      scope: [
        'https://www.googleapis.com/auth/plus.login',
        'https://www.googleapis.com/auth/userinfo.email'
      ]
    })
  );

  // GET /auth/google/callback
  app.get(
    '/auth/google/callback',
    passport.authenticate('google', {
      session: false,
      failureRedirect: webHost + '/login'
    }),
    function(req, res) {
      const payload = {
        user: {
          id: req.user._id
        }
      };

      jwt.sign(
        payload,
        config.get('jwtSecret'),
        { expiresIn: 36000 },
        (err, token) => {
          if (err) throw err;
          return res.redirect(webHost + '/social/google/' + token);
        }
      );
    }
  );
};

module.exports = configurePassport;

Heroku Logs

2019-08-20T20:04:52.264433+00:00 app[web.1]: FacebookTokenError: Can't Load URL: The domain of this URL isn't included in the app's domains. To be able to load this URL, add all domains and subdomains of your app to the App Domains field in your app settings.
2019-08-20T20:04:52.264453+00:00 app[web.1]: at Strategy.parseErrorResponse (/app/node_modules/passport-facebook/lib/strategy.js:198:12)
2019-08-20T20:04:52.264456+00:00 app[web.1]: at Strategy.OAuth2Strategy._createOAuthError (/app/node_modules/passport-oauth2/lib/strategy.js:405:16)
2019-08-20T20:04:52.264460+00:00 app[web.1]: at /app/node_modules/passport-oauth2/lib/strategy.js:175:45
2019-08-20T20:04:52.264462+00:00 app[web.1]: at /app/node_modules/oauth/lib/oauth2.js:191:18
2019-08-20T20:04:52.264465+00:00 app[web.1]: at passBackControl (/app/node_modules/oauth/lib/oauth2.js:132:9)
2019-08-20T20:04:52.264466+00:00 app[web.1]: at IncomingMessage.<anonymous> (/app/node_modules/oauth/lib/oauth2.js:157:7)
2019-08-20T20:04:52.264469+00:00 app[web.1]: at IncomingMessage.emit (events.js:203:15)
2019-08-20T20:04:52.264470+00:00 app[web.1]: at endReadableNT (_stream_readable.js:1145:12)
2019-08-20T20:04:52.264472+00:00 app[web.1]: at process._tickCallback (internal/process/next_tick.js:63:19)

来源:https://stackoverflow.com/questions/57634417/passport-js-tokenerror-after-deployment-to-production

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!