You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google OAuth Refresh Token始终为Undefined问题求助(附代码)

Google OAuth refreshToken 返回 undefined 问题求助

我知道这个问题被提过很多次,但试过所有可行方案后,我的refreshToken仍然返回undefined。已经执行了以下操作:

  • 删除应用在第三方访问权限中的记录;
  • 删除MongoDB中对应的用户条目;
  • 在Passport配置中设置accessType: 'offline';
  • 尝试添加prompt: 'consent'和approvalPrompt: 'force'参数;
  • 用passport.authorize替换passport.authenticate;
  • 移除离线访问配置后,在回调URL后拼接?access_type=offline。

补充说明:应用目前在Google开发者控制台处于测试状态,尚未发布。OAuth授权环节没有出现offline_access的授权选项。

我正在开发一个MERN栈应用,允许用户关联谷歌账号并调度YouTube直播视频,当前专注于Node.js后端开发。相关代码如下:

oauth/passportConfig.js

require('dotenv').config();

const passport = require("passport");
const axios = require("axios");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const GoogleUser = require("../models/usergoogleModel");

// Initialize Passport.js
passport.initialize();

// Configure Passport.js with Google OAuth 2.0 Strategy
passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      callbackURL: process.env.GOOGLE_CALLBACK_URL,
      scope: [
        "https://www.googleapis.com/auth/youtube",
        "https://www.googleapis.com/auth/youtube.force-ssl",
        "https://www.googleapis.com/auth/youtube.upload",
      ],
      skipUserProfile: true,
      accessType: 'offline'
    },
    async (accessToken, refreshToken, _profile, done) => {
      try {
        console.log("Refresh Token:", refreshToken);

        // Make a request to the YouTube API to get the user's profile information
        const response = await axios.get(
          "https://www.googleapis.com/youtube/v3/channels",
          {
            params: {
              part: "snippet,contentDetails,statistics",
              mine: "true",
            },
            headers: {
              Authorization: `Bearer ${accessToken}`,
            },
          }
        );

        const youtubeProfile = response.data.items[0];
        const googleId = youtubeProfile.id;

        // Find or create the user based on googleId
        let user = await GoogleUser.findOne({ googleId });
        if (!user) {
          user = new GoogleUser({
            googleId,
            accessToken,
            refreshToken,
          });
        } else {
          // Update user's access token and refresh token
          user.accessToken = accessToken;
          user.refreshToken = refreshToken;
        }
        await user.save();
        done(null, user);
        
      } catch (err) {
        console.error(
          "Error in OAuth callback:",
          err.response ? err.response.data : err.message
        );
        done(err);
      }
    }
  )
);

// Implement Passport.js Serialization and Deserialization
passport.serializeUser((user, done) => {
  done(null, user);
});
passport.deserializeUser(async (id, done) => {
  try {
    const user = await GoogleUser.findById(id);
    if (!user) {
      // If user is not found, pass null as the user
      return done(null, null);
    }
    done(null, user);
  } catch (err) {
    done(err);
  }
});

module.exports = passport;

controllers/authgoogleController.js

const passport = require("passport");

// Controller function to initiate Google OAuth authentication
exports.googleAuth = passport.authenticate("google", {
});

// Callback function for Google OAuth authentication
exports.googleAuthCallback = (req, res, next) => {
  passport.authenticate("google", (err, user, info) => {
    if (err) {
      return next(err);
    }
    if (!user) {
      // Handle authentication failure
      //  return res.redirect("/login"); // Redirect to the login page or handle appropriately
      return res.send("Login failed.");
    }
    // Handle authentication success
    res.send("Google OAuth authentication successful!"); // Send a response indicating successful authentication
  })(req, res, next);
};

// Controller function for user logout
exports.logout = (req, res) => {
  req.logout(() => {
    //    res.redirect("/"); // Redirect to the home page after logout
    res.send("Logout successful!");
  });
};

routes/authgoogle.js

const express = require("express");
const router = express.Router();
const authgoogleController = require("../controllers/authgoogleController");

// Route for initiating Google OAuth authentication
router.get("/", authgoogleController.googleAuth);

// Route for handling the OAuth callback
router.get("/callback", authgoogleController.googleAuthCallback);

// Route for user logout
router.get("/logout", authgoogleController.logout);

module.exports = router;

models/usergoogleModel.js

const mongoose = require("mongoose");
const usergoogleSchema = new mongoose.Schema({
  googleId: String,
  accessToken: String,
  refreshToken: String,
});

module.exports = mongoose.model("GoogleUser", usergoogleSchema);

server.js

require("dotenv").config();

const express = require("express");
const session = require("express-session");
const mongoose = require("mongoose");
const passport = require("./oauth/passportConfig")
const authgoogleRoutes = require("./routes/authgoogle");

// Express app
const app = express();

// Middleware to initialize Passport and configure session
app.use(
  session({
    secret: process.env.SESSION_SECRET,
    resave: false,
    saveUninitialized: true,
  })
);

app.use(passport.session());

//  Routes
app.use("/auth/google", authgoogleRoutes);

// Connect to db
mongoose
  .connect(process.env.MONGO_URI)
  .then(() => {
    // Listen for requests
    app.listen(process.env.PORT, () => {
      console.log("Connected to the db & listening on port", process.env.PORT);
    });
  })
  .catch((error) => {
    console.log(error);
  });

内容的提问来源于stack exchange,提问作者threehappypenguins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 11:32:05