1

如果这个问题已经得到解答,我提前道歉,我花了大约 30 分钟搜索,但找不到遇到类似问题的人。

情况:我已经结合 Passport.js 创建了一个 React/Express 应用程序来对用户进行身份验证。

问题:虽然该应用程序在本地服务器上运行良好,但一旦将其部署到 Heroku,护照“谷歌”身份验证流程将不会启动。我在下面包含了一个链接,请尝试单击“登录”以复制该问题。页面不会被踢入身份验证流程,而是会重新加载,尽管 URL 为:'/auth/google'

Heroku 链接:https ://stormy-hamlet-86069.herokuapp.com

感谢您抽出宝贵的时间 :)

以下是相关的服务器端代码:

// index.js

const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const app = express();

const { headerSettings } = require('./settings/headerSettings');
const { searchProperties } = require('./services/controllers');

const keys = require('./config/keys');
require('./models/user');
require('./services/passport');

app.use(
    cookieSession({
        maxAge: 30 * 24 * 60 * 1000,
        keys: [keys.cookieKey]
    })
);

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

app.get('/test', (req, res) => {
    res.send({ test: 'working' });
});

app.get('/api/search', searchProperties);

// authRoutes(app);

app.get(
    '/auth/google',
    // This is where I tell passport to use the strategy: 'google'.
    passport.authenticate('google', {
        // scope specifies the information my app would have access to.
        scope: ['profile', 'email']
    })
);

/* These two route handlers look very similar, 
     * however when passport.authenticate is run this time there is code included in the req,
     * so passport knows that it should behave differently.
     * Instead of kicking the user into the oAuth flow it exchanges the code for user profile. */
app.get(
    '/auth/google/callback',
    passport.authenticate('google'),
    // After the user has been authenticated, they must be redirected to the correct part of the app.
    (req, res) => {
        res.redirect('/auth/');
    }
);

app.get('/api/logout', (req, res) => {
    // This function removes the cookie from any future req objects.
    req.logout();
    res.redirect('/');
});

app.get('/api/current_user', (req, res) => {
    res.send(req.user);
});

// This should only be triggered in the Heroku environment.
if (process.env.NODE_ENV === 'production') {
    // Setting Express to serve up production assets - main.js or main.css
    app.use(express.static('client/build'));
    // Express will serve up the index.html file if it doesn't recognise the route.
    const path = require('path');
    app.get('*', (req, res) => {
        res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
    });
}

app.use(bodyParser.urlencoded({ extended: true }));

app.use(bodyParser.json());

app.use(headerSettings);

mongoose.connect(keys.mongoURI);
mongoose.connection
    .once('open', () => console.log('Connected to MongoDB:Atlas'))
    .on('error', error => console.warn('Warning', error));

const PORT = process.env.PORT || 5000;
app.listen(PORT);

// 护照.js

const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const mongoose = require('mongoose');

const keys = require('../config/keys');

const User = mongoose.model('users');

passport.serializeUser((user, done) => {
    // user.id is the mongo user id, not the google profile.id.
    done(null, user.id);
});

passport.deserializeUser((id, done) => {
    User.findById(id).then(user => {
        done(null, user);
    });
});

passport.use(
    new GoogleStrategy(
        {
            clientID: keys.googleClientID,
            clientSecret: keys.googleClientSecret,
            // After the user grants permission for our App, we need to specify the route they will be sent back to.
            callbackURL: '/auth/google/callback',
            // Fix Heroku Proxy issue.
            proxy: true
        },
        // This callback function is executed anytime a user is returned from the Google oAuth flow.
        async (accessToken, refreshToken, profile, done) => {
            const existingUser = await User.findOne({ googleId: profile.id });
            if (existingUser) {
                return done(null, existingUser);
            }
            const user = await new User({ googleId: profile.id }).save();
            done(null, user);
        }
    )
);

以下是相关的客户端代码:

// 登录

import React, { Component } from "react";
import { connect } from "react-redux";
// import { Link } from "react-router-dom";

class Login extends Component {
  renderContent() {
    switch (this.props.auth) {
      case null:
        return;
      case false:
        return (
          // The reason anchor <a> tags are being used in because the user should be directed to a completely new page.
          <li>
            <a className="login__button--li" href="/auth/google">
              Login
            </a>
          </li>
        );
      default:
        return (
          // The reason anchor <a> tags are being used in because the user should be directed to a completely new page.
          <li>
            <a className="login__button--li" href="/api/logout">
              Logout
            </a>
          </li>
        );
    }
  }
  selectIconLink() {
    return this.props.auth ? "/surveys" : "/";
  }
  render() {
    return (
      <nav>
        <div className="login">
          <ul className="login__button">{this.renderContent()}</ul>
        </div>
      </nav>
    );
  }
}

// Return an object that will be passed to the Header as props.
const mapStateToProps = ({ auth }) => ({ auth });
export default connect(mapStateToProps)(Login);
4

0 回答 0