【问题标题】:CORS error while implementing google Oauth2 (MERN)实现 google Oauth2 (MERN) 时出现 CORS 错误
【发布时间】:2021-06-29 19:56:26
【问题描述】:

我已经在一个 mern 网络应用程序中使用 passport.js 实现了本地和谷歌登录。本地身份验证在前端工作正常,但在使用 Google 策略时出现错误。

错误
在“https://accounts.google.com/o/oauth2/v2/auth?.........”访问 XMLHttpRequest(重定向自“http://localhost:5000/auth/google”)来自原点“http://localhost:3000”的 CORS 策略已阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头。

错误:网络错误
在 createError (createError.js:16)
在 XMLHttpRequest.handleError (xhr.js:84)

控制台显示的问题
通过指定 cookie 的 SameSite 属性来指示是否要在跨站点上下文中设置 cookie。
通过指定其 SameSite 属性来指示是否在跨站点请求中发送 cookie

我使用 POSTMAN 测试了 google 策略,它运行良好,但是从我的前端请求时似乎出现了一些问题。

server.js

require("dotenv").config();
const express=require("express");
const cors=require("cors");
const mongoose=require("mongoose");
const session=require("express-session");
const passport=require("passport");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const errorController=require("./controllers/errorController")


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

app.use(
  cors({
    origin: "http://localhost:3000", // <-- location of the react app were connecting to
    credentials: true,
  })
);
app.use(express.static("public"));
app.use(express.urlencoded({extended: true}));
app.use(express.json());



app.use(session({
    secret:process.env.SECRET,
    resave:false,
    saveUninitialized:false,
}));

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

const User=require("./models/user.model");
passport.use(User.createStrategy());

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


passport.use(new GoogleStrategy({
    clientID: process.env.CLIENT_ID,
    clientSecret: process.env.CLIENT_SECRET,
    callbackURL: "http://localhost:5000/auth/google/keeper"
  },
  function(accessToken, refreshToken, profile, cb) {
      console.log(profile);
    User.findOrCreate({ googleId: profile.id }, function (err, user) {
      return cb(err, user);
    });
  }
));

mongoose.connect(process.env.MONGO_URI, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify:false, useCreateIndex:true});


const connection = mongoose.connection;
connection.once('open', () => {
  console.log("MongoDB database connection established successfully");
});


const loginRouter=require("./routes/login");
const registerRouter=require("./routes/register");
const logoutRouter=require("./routes/logout");
const authRouter=require("./routes/auth");



app.use("/login", loginRouter);
app.use("/register", registerRouter);
app.use("/logout", logoutRouter);
app.use("/auth/google", authRouter);

app.use(errorController);

app.listen(port, function(){
    console.log("server started on port 5000");
});

auth.js(处理谷歌登录相关路由)

const router=require("express").Router();
const passport=require("passport");


router.get("/", passport.authenticate("google", { scope: ["profile"] }));

router.get("/keeper", 
  passport.authenticate("google", { failureRedirect: "/login" }),
  function(req, res) {
    // Successful authentication, redirect home.
    res.redirect("/");
  });

  module.exports=router;

来自前端的axios请求

function googleLogin(event){
      Axios({
        method: "GET",
        withCredentials: true,
        url: "http://localhost:5000/auth/google",
      })
      .then(function(res){
        console.log(res);
      })
      .catch(function(err){
        console.log(err);
      })
      event.preventDefault();
    }

触发请求的按钮

<button className="btn btn-danger" onClick={googleLogin}>Sign in with Google</button>

【问题讨论】:

  • 将 Access-Control-Allow-Origin 值添加到 * 或您的 FE url 。然后它会工作。
  • @AmaranadhMeda 我在 cors 中间件中添加了原始值作为前端 URL。请查看 server.js 文件。我不能将它标记为 * 因为我在我的 axios 请求中将 withCredentials 设置为 true。自 3 天以来,我一直被这个问题困扰:'(
  • 在 /login route configuratio 之前添加 cors 配置。

标签: node.js reactjs mongodb express passport.js


【解决方案1】:

您不能对 /auth/google URL 进行 axios 调用!!
这是我的解决方案...

// step 1:
// onClick handler function of the button should use window.open instead 
// of axios or fetch
const googleLogin = () => window.open("http://[server:port]/auth/google", "_self")

//step 2: 
// on the server's redirect route add this successRedirect object with correct url. 
// Remember! it's your clients root url!!! 
router.get(
    '/google/redirect', 
    passport.authenticate('google',{
        successRedirect: "[your CLIENT root url/ example: http://localhost:3000]"
    })
)

// step 3:
// create a new server route that will send back the user info when called after the authentication 
// is completed. you can use a custom authenticate middleware to make sure that user has indeed 
// been authenticated
router.get('/getUser',authenticated, (req, res)=> res.send(req.user))

// here is an example of a custom authenticate express middleware 
const authenticated = (req,res,next)=>{
    const customError = new Error('you are not logged in');
    customError.statusCode = 401;
    (!req.user) ? next(customError) : next()
}
// step 4: 
// on your client's app.js component make the axios or fetch call to get the user from the 
// route that you have just created. This bit could be done many different ways... your call.
const [user, setUser] = useState()
useEffect(() => {
    axios.get('http://[server:port]/getUser',{withCredentials : true})
    .then(response => response.data && setUser(response.data) )
},[])

解释....
步骤 1 将在您的浏览器上加载您的服务器身份验证 URL 并发出身份验证请求。
步骤 2 然后在认证完成时在浏览器上重新加载客户端 url 完成。
步骤 3 使 api 端点可用于收集用户信息以更新反应状态
步骤 4 调用端点,获取数据并更新用户状态。

【讨论】:

    猜你喜欢
    • 2017-11-21
    • 1970-01-01
    • 2015-07-17
    • 2020-09-07
    • 2021-06-18
    • 2019-02-08
    • 2019-01-10
    • 2021-11-18
    • 1970-01-01
    相关资源
    最近更新 更多