【问题标题】:Heroku Google OAuth ErrorHeroku Google OAuth 错误
【发布时间】:2019-01-01 03:40:15
【问题描述】:

我遇到了一个非常令人沮丧的错误。当我在本地开发环境中发出 google OAuth 请求时,它运行良好。 (使用护照认证)。推送到 Heroku 我得到一个状态 200 而我在开发中得到的状态 302 将我重定向到 google oauth 登录页面。屏幕只是显示空白,没有错误。我试图故意在客户端 ID 上加上一个错误,但它甚至根本没有注册请求。 Log-In 将我带到 heroku 上的空白屏幕,并且根本没有注册任何请求。 请帮忙!

服务器端护照:

 // .use is generic register
passport.use(
  new GoogleStrategy(
    {
      clientID: keys.googleClientID,
      clientSecret: keys.googleClientSecret,
      // need url for where user should go on callback after they grant permission to our application on google auth page
      callbackURL: "/auth/google/callback",
      // have to authorize this callback url in the google oauth console.developors screen because of security reasons
      proxy: true // trust the proxy our request runs through so heroku callbacks to the correct url
    },
    async (accessToken, refreshToken, profile, done) => {
      // after authenticated on the next get request to google it will call this with the accessToken, aka callback function
      // console.log("access token", accessToken);
      // console.log("refresh token", refreshToken);
      // console.log("profile", profile);

      // check to see if user id already exists before saving it to DB so it does not overlap...mongoose query...asynchronous operation
      // using async await
      const existingUser = await User.findOne({
        googleId: profile.id
      });
      // get promise response
      if (existingUser) {
        // already have record
        // finish passport auth function
        return done(null, existingUser); // passes to serialize user so serialize can pull that user id
      }
      // we don't have a new record so make one
      const user = await new User({
        // creates new model instance of user
        googleId: profile.id
      }).save(); // have to save it to DB
      // get promise from save since asynchronize, then finish with response
      done(null, user); // passes to serialize user so serialize can get that id
    }
  )
); // create new instance of GoogleStrategy

服务器端 API:

    app.get(
    "/auth/google", // passport, attempt to authenticate the user coming in on this route
    passport.authenticate("google", {
      // google strategy has internal code, that is 'google', so passport will know to find the google passport authenticator
      scope: ["profile", "email"] // options object
      // specifies to google we want access to this users profile and email information from their account, these are premade strings in the google oauth process not made up
    })
  );

  // in this callback route they are going to have the code, and google will see that and it will handle it differnetly by exchanging the code for an actual profile, it will call the next part of the GoogleStrategy, aka the accessToken to be saved to Database

  // @route GET auth/google/callback
  // @desc  Get callback data from google to redirect user if signed in
  // @access Private can only access this after signed in

  app.get(
    "/auth/google/callback",
    passport.authenticate("google"),
    // after authenticate process is done, send user to correct route
    (req, res) => {
      // redirect to dashboard route after sign-in
      res.redirect("/surveys");
      // full HTTP requrest, so it reloads versus AJAX request which uses react and redux and is much faster
    }
  );

客户端 - 端

<div
            className="collapse navbar-collapse nav-positioning"
            id="navbarNav"
          >
            <ul className="navbar-nav">
              <li className="nav-item google-link">
                <a className="nav-link" href="/auth/google">
                  Google Login
                </a>
              </li>
            </ul>
          </div>

索引.js

// Route file, or starter file
const express = require("express");
// node.js does not have support from E6,
// so we use common js modules
// import vs require :
// common vs ES6

// bring in mongoose
const mongoose = require("mongoose");

// tell express it must make use of cookies when using passport
const cookieSession = require("cookie-session");
const passport = require("passport");

// pull in body-parser middleware to get req.body
const bodyParser = require("body-parser");

// connect it to DB in keys so it is not posted to github
const keys = require("./config/keys");

//connect mongoose
mongoose.connect(keys.mongoURI);

// ########## MODELS ################
// THIS MUST BE ABOVE WHERE YOU USE IT, SO ABOVE PASSPORT
require("./models/User");
require("./models/Survey");
// don't have to require recipient because its included inside Survey

// pull in passport service, we are not returning anything in passport, so we do not need const passport because nothing to assign
require("./services/passport");

// Generate a new application that represents a running express app
const app = express(); // vast majority use single app
// this will listen for incoming requests, and route them on to different route handlers

// parser so every time a req has a req.body comes in then it will be assigned to the req.body property
app.use(bodyParser.json());

app.use(
  cookieSession({
    // age for auth cookies to last... 30 days
    maxAge: 30 * 24 * 60 * 60 * 1000,
    // give cookie a key
    keys: [keys.cookieKey]
  })
);

// tell passport to use cookies
app.use(passport.initialize());
app.use(passport.session());
// done with authentication flow

//require that file returns a function, which is then immediately called with the app object
require("./routes/authRoutes")(app);
require("./routes/billingRoutes")(app);
require("./routes/surveyRoutes")(app);

if (process.env.NODE_ENV === "production") {
  // if in production make sure express will serve up production assets
  // like main.js
  app.use(express.static("client/build"));

  // Express will serve up index.html file if it doesn't recognize the routes
  const path = require("path");

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

// dynamically figure out what port to listen to... Heroku, heroku will inject env variables in moment of deploy, but only works in production not development environment
const PORT = process.env.PORT || 5000; // if heroku port exists assign it that, else, assign it 5000

app.listen(PORT); // listen for requests and route them to the correct handler on port 5000

/* ###### HEROKU PREDEPLOY ##### */
// specifiy node version and start script for heroku in package.json
// make .gitignore for dependencies which should not be committed on deploy, heroku will install them itself

// app.use wires up middleware for our application

// ############### TIPS
/*
 Google first, because its been asked before...
 Run in module
 */

【问题讨论】:

  • 您能否为/auth/google 发布您的客户端代码和服务器端API?
  • 抱歉耽搁了,已发布
  • 您的 Google 客户端密钥是否配置为与 heroku 域一起使用?
  • 是的,他们是......即使他们不是我也会收到授权错误?它只是把我带到一个空白屏幕。同样,这可以在本地正常工作,但不能在 heroku 上正常工作。我会错过制作设置吗?
  • 您需要进入浏览器开发工具并查看网络选项卡以查看发生了什么

标签: javascript reactjs heroku google-oauth status


【解决方案1】:

我认为问题在于您在 heroku 上的应用仅在侦听 http 请求。如果您指向 OAuth 页面的链接的格式为“https://your-domain.com/auth/google”,那么您的应用程序的路由将与该路由不匹配(​​因为 https),因此您的应用程序将显示一个空白页面,就像它会显示任何路由一样它没有在听。

解决此问题并仍使用 https(因此仍会在 url 旁边显示安全徽标)的一种方法是对除此 OAuth 链接之外的每个链接使用 https。您在应用程序中的 get 和 post 请求将使用 http,但 url 上可见的任何链接都将使用 https。这样的事情会起作用:

app.use(function(req, res, next) {
        if (process.env.NODE_ENV === "production") {
            const reqType = req.headers["x-forwarded-proto"];
            // if not https redirect to https unless logging in using OAuth
            if (reqType !== "https") {
                req.url.indexOf("auth/google") !== -1
                  ? next()
                  : res.redirect("https://" + req.headers.host + req.url);
            } 
        } else {
            next();
        }
    });  

任何指向 OAuth 登录页面的前端链接都应该是 http 链接

【讨论】:

    【解决方案2】:

    这个问题我也有同样的问题。我通过在配置键上定义一个 absoluteURI 来解决它。因为谷歌在 https:// 和 heroku 路径中查看 url 回调是 http:// 当你添加代理时应该修复它: true 但不是。

    在配置键上添加

    dev: absoluteURI: localhost:5000

    产品:absoluteURI:http://herokupath

    // .use is generic register
    passport.use(
      new GoogleStrategy(
        {
          clientID: keys.googleClientID,
          clientSecret: keys.googleClientSecret,
          callbackURL: absoluteURI + "/auth/google/callback",
          proxy: true 
        },
    

    【讨论】:

      【解决方案3】:

      请看一下这个SO answer。看起来您的范围参数需要修改才能使 google auth 正常工作。

      【讨论】:

      • 查看该答案下方的评论,这是设置范围的另一种方式。我会测试这个方法,也许他们在开发中工作,但在 heroku 中没有。
      • 仍然无法正常工作,我不确定发生了什么,因为没有显示错误。
      • 似乎它甚至没有达到我的 api 请求
      猜你喜欢
      • 1970-01-01
      • 2014-05-03
      • 2014-02-09
      • 2015-04-03
      • 2016-08-11
      • 1970-01-01
      • 1970-01-01
      • 2020-11-08
      • 2019-07-14
      相关资源
      最近更新 更多