Heroku谷歌OAuth错误

问题描述 投票:2回答:3

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

服务器端Passport:

 // .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>

Index.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
 */
javascript reactjs heroku google-oauth status
3个回答
1
投票

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

解决此问题仍然使用https(因此仍然显示网址旁边的安全徽标)的一种方法是对除此OAuth链接之外的每个链接使用https。您在应用内的获取和发布请求将使用http,但网址上显示的任何链接都将使用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链接


0
投票

请看看这个SO answer。看起来您的范围参数需要修改才能让Google身份验证工作。


0
投票

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

在配置键上添加

dev:absoluteURI:localhost:5000

prod:absoluteURI:http://herokupath

// .use is generic register
passport.use(
  new GoogleStrategy(
    {
      clientID: keys.googleClientID,
      clientSecret: keys.googleClientSecret,
      callbackURL: absoluteURI + "/auth/google/callback",
      proxy: true 
    },
© www.soinside.com 2019 - 2024. All rights reserved.