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

已完成Google认证仍触发GaxiosError: Login Required问题求助

Gmail API调用报错"Login Required"的解决方法

问题描述

完成Google OAuth2认证流程后,调用gmail.users.messages.list接口时,仍收到错误:GaxiosError: Login Required。相关代码如下:

原index.js

const express = require("express");
const cors = require("cors");
const passport = require("passport");
const cookieSession = require("cookie-session");
const { google } = require('googleapis');
const path = require('path');

require("./passport-setup");

const CLIENT_URL = "http://localhost:3000/inbox";
const passportSetup = require("./passport-setup");
const GoogleStrategy = require("./passport-setup").OAuth2Strategy;

const app = express();
const gmail = google.gmail({ version: 'v1' });

app.use(cors({credentials: true, origin: 'http://localhost:3000'}));
app.use(express.urlencoded({ extended: false }));
app.use(express.json());

app.use(
  cookieSession({
    name: "session",
    keys: ["key1", "key2"],
  })
);

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

app.get("/", (req, res) => res.send("Hello World"));
app.get("/failed", (req, res) => res.send("Failed!"));

app.get("/google", passport.authenticate('google', {
  scope: ['profile',
      'email',
      'https://mail.google.com/'
  ],
  accessType: 'offline',
  prompt: 'consent'
  }))

app.get("/google/callback", passport.authenticate("google", {
  successRedirect: CLIENT_URL, 
  failureRedirect: "/failed"
}))

app.get("/success", (req,res)=>{
  if(req.user) {
      res.status(200).json({
          success: true,
          message: "Login success!",
          user: req.user,
          cookies: req.cookies
  });
};
});

app.get("/inbox", async (req, res) => {
  try {
    const userId = 'me';
    const accessToken = req.user.accessToken; // Assuming the access token is stored in req.user

    const response = await gmail.users.messages.list({
      userId,
      maxResults: 10,
      auth: accessToken, // Include the access token in the request
    });

    res.json(response.data);
  } catch (error) {
    console.error("Error fetching Gmail messages:", error);
    res.status(500).json({ error: "Failed to fetch Gmail messages" });
  }
});

app.get("/logout", (req,res)=>{
  req.logout();
  res.redirect(CLIENT_URL);
})
app.listen(5000, () => console.log(`Server running at port ${5000}!`));

原passport-setup.js

const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;

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

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

passport.use(
  new GoogleStrategy(
    {
      clientID: "insert client id",
      clientSecret: "insert client secret",
      callbackURL: "http://localhost:5000/google/callback",
    },
    function (accessToken, refreshToken, profile, done) {
      return done(null, profile);
    }
  )
);

问题根源

  1. Passport未存储访问令牌:在GoogleStrategy的验证回调中,仅将profile存入会话,丢失了accessToken和refreshToken,导致req.user.accessToken不存在。
  2. Google API认证参数错误:直接传递字符串格式的token给auth参数不符合googleapis库要求,需要传入OAuth2客户端实例。

修复方案

1. 修改Passport逻辑,保存令牌信息

更新passport-setup.js的验证回调,将令牌与用户信息合并存储:

const passport = require("passport");
const GoogleStrategy = require("passport-google-oauth20").Strategy;

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

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

passport.use(
  new GoogleStrategy(
    {
      clientID: "insert client id",
      clientSecret: "insert client secret",
      callbackURL: "http://localhost:5000/google/callback",
    },
    function (accessToken, refreshToken, profile, done) {
      // 合并令牌与用户信息
      const user = {
        ...profile,
        accessToken,
        refreshToken
      };
      return done(null, user);
    }
  )
);

2. 正确配置Google API认证

修改index.js的/inbox路由,使用OAuth2客户端实例完成认证,同时添加令牌过期自动刷新逻辑:

// 顶部新增OAuth2客户端初始化
const { google } = require('googleapis');
const oauth2Client = new google.auth.OAuth2(
  "insert client id", // 替换为你的Client ID
  "insert client secret" // 替换为你的Client Secret
);

// 修改/inbox路由
app.get("/inbox", async (req, res) => {
  try {
    if (!req.user?.accessToken) {
      return res.status(401).json({ error: "用户未授权或令牌缺失" });
    }

    // 设置客户端凭证
    oauth2Client.setCredentials({
      access_token: req.user.accessToken,
      refresh_token: req.user.refreshToken
    });

    const response = await gmail.users.messages.list({
      userId: 'me',
      maxResults: 10,
      auth: oauth2Client, // 使用OAuth2客户端实例
    });

    res.json(response.data);
  } catch (error) {
    console.error("获取Gmail邮件失败:", error);
    // 处理令牌过期,自动刷新
    if (error.code === 401 && req.user.refreshToken) {
      try {
        const { credentials } = await oauth2Client.refreshAccessToken();
        // 更新会话中的令牌
        req.user.accessToken = credentials.access_token;
        // 重新请求API
        const response = await gmail.users.messages.list({
          userId: 'me',
          maxResults: 10,
          auth: oauth2Client,
        });
        res.json(response.data);
      } catch (refreshError) {
        res.status(401).json({ error: "令牌过期且刷新失败,请重新登录" });
      }
    } else {
      res.status(500).json({ error: "获取Gmail邮件失败" });
    }
  }
});

额外注意事项

  • 确认Google Cloud Console已启用Gmail API
  • 认证scope已包含https://mail.google.com/(原代码已正确设置)
  • 可通过/success接口验证req.user是否包含accessToken和refreshToken

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 00:57:42