在 React/Express 中使用 PassportJS 进行 Google OAuth 时出现 CORS 错误

Posted

技术标签:

【中文标题】在 React/Express 中使用 PassportJS 进行 Google OAuth 时出现 CORS 错误【英文标题】:Getting CORS error when using PassportJS for Google OAuth in React/Express 【发布时间】:2021-08-27 01:27:44 【问题描述】:

尽管找了几个小时,但我还是找不到简单的 cors 错误的解决方案。我在浏览器中得到以下内容:

在 'https://accounts.google.com/o/oauth2/v2/auth?response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A3001%2Fapi%2Fauth%2Fauth%2Fgoogle%2Fcallback&scope= 访问 XMLHttpRequest profile%20email&client_id=******.apps.googleusercontent.com' (从 'http://localhost:3000/api/auth/auth/google' 重定向)来自 'http://localhost:3000'已被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。

出于安全原因,我刚刚为这篇文章替换了客户端 ID。 当我在 Postman/Thunder Client 中进行此调用时,它会返回 200 响应并且似乎工作正常

所以我尝试在 express app.use(cors()); 中使用 cors NPM 包也尝试在 server.js app.all("/*", function (req, res, next) res.header("Access-Control-Allow-Origin", "*"); next(); ); 中使用此功能我也尝试使用`

const proxy = require('http-proxy-middleware');

module.exports = function(app) 
    app.use(proxy("/api/auth/auth/google", 
          target: "https://localhost:3000/",
          changeOrigin: true
    ));

我刚刚完成了我在 SO 上找到的书中的所有解决方案,但到目前为止似乎没有一个可行。任何帮助将不胜感激这是我的代码:

Package.json在客户端


  "name": "tasktracker",
  "proxy": "http://localhost:3001/",
  "version": "0.1.0",
  "private": true,
  "dependencies": 
    "@testing-library/jest-dom": "^5.13.0",
    "@testing-library/react": "^11.2.7",
    "@testing-library/user-event": "^12.8.3",
    "axios": "^0.21.1",
    "http-proxy-middleware": "^2.0.0",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^5.2.0",
    "react-scripts": "4.0.3",
    "web-vitals": "^1.1.2"
  ,
  "scripts": 
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  ,
  "eslintConfig": 
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  ,
  "browserslist": 
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  

Homepage.js

import React from "react";
import API from "../components/utils/API";

export default function HomePage() 
  const handleLogin = () => 
    API.login();
  ;
  const testing = () => 
    console.log("Hello world!");
    API.testing();
  ;

  return (
    <div>
      <h1>Let's login with Google</h1>
      <button onClick=handleLogin>Login</button>
      <button onClick=testing>Testing</button>
    </div>
  );

API.js

import axios from "axios";
export default 
  login: function () 
    return axios.get("/api/auth/auth/google");
  ,
  testing: function () 
    return axios.get("/api/auth/test");
  ,
;

Server.js

const express = require("express");
const passport = require("passport");
const routes = require("./routes");
const app = express();
const PORT = process.env.PORT || 3001;
const cors = require("cors");

app.use(cors());

// Define middleware here
app.use(express.urlencoded( extended: true ));
app.use(express.json());
require("./config/passport");
app.use(passport.initialize());

// Add routes, both API and view
app.use(routes);


// Start the API server
app.listen(PORT, function () 
  console.log(`????  ==> API Server now listening on PORT $PORT!`);
);

auth.js(认证路由)

const router = require("express").Router();
const passport = require("passport");
const cors = require("cors");
const CLIENT_HOME_PAGE_URL = "http://localhost:3000";

/Route is api/auth/test
router.get("/test", function (req, res) 
  res.send(console.log("Test to backend"));
);


//Route is api/auth/auth/google/callback
router.get(
  "/auth/google/callback",
  passport.authenticate("google", 
    successRedirect: CLIENT_HOME_PAGE_URL,
    failureRedirect: "/",
    session: false,
  ),
  function (req, res) 
    var token = req.user.token;
    res.redirect("http://localhost:3000?token=" + token);
  
);


  //Route is api/auth/auth/google/
router.get(
  "/auth/google",

  passport.authenticate("google",  scope: ["profile", "email"] )
);

module.exports = router;

Passport.js

var passport = require("passport");
var GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;

passport.serializeUser(function (user, done) 
  done(null, user);
);

passport.deserializeUser(function (user, done) 
  done(null, user);
);

passport.use(
  new GoogleStrategy(
    
      clientID:
        "*****",
      clientSecret: "*****",
      callbackURL: "http://localhost:3001/api/auth/auth/google/callback",
    ,
    function (accessToken, refreshToken, profile, done) 
      var userData = 
        email: profile.emails[0].value,
        name: profile.displayName,
        token: accessToken,
      ;
      done(null, userData);
    
  )
);

我知道这篇文章有点长,如果有人有任何想法或解决方案会对我有很大帮助。

【问题讨论】:

【参考方案1】:

我最近遇到了同样的问题。使用

window.location.href = "http://localhost:5000/api/auth/google" 

而不是

axios.get("/api/auth/auth/google") 

解决了问题。

认证成功后,如果您想将用户重定向到用户发出登录请求的原始网址,您可以这样做...

const currentUrl = window.location.href
const encodedParam = encodeURI(`?redirectUrl=$currentUrl`)
window.location.href = `http://localhost:5000/api/auth/google$encodedParam`

将重定向值存储在会话中

req.session.redirectPath = req.query.redirectUrl

身份验证后,在您的回调路由中使用它

【讨论】:

以上是关于在 React/Express 中使用 PassportJS 进行 Google OAuth 时出现 CORS 错误的主要内容,如果未能解决你的问题,请参考以下文章

create-react-app/express 代理错误:无法代理请求(ECONNRESET)

如何在 React/Express 应用程序中实现可选身份验证?

与 react、express、axios、node 和 mysql 相关的 CORS 问题

BrowserslistError:React/Express 应用程序中的未知浏览器查询 `dead`

TypeError:无法读取未定义 React Express 的属性“原型”

不允许 CORS 原始请求 React Express