如果这个问题已经得到解答,我提前道歉,我花了大约 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);