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

基于Google Drive API的开源项目:如何在Next.js中结合next-auth实现gapi-script认证?

结合 next-auth 与 gapi-script 实现 Google Drive API 认证

我明白你现在的困境——已经分别搞定了 gapi-script 独立认证和 next-auth 的 Google 登录,但不知道怎么把两者串起来复用会话对吧?其实核心思路是利用 next-auth 拿到的 OAuth Access Token 来给 gapi 授权,而不是让 gapi 再单独走一遍认证流程。下面我给你一步步拆解实现方案:

第一步:配置 NextAuth 的 GoogleProvider,请求 Drive API 所需权限

首先要确保 next-auth 在发起 OAuth 请求时,已经获取到 Google Drive API 需要的权限范围。修改你的 [...nextauth].js 配置:

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
      // 这里添加你需要的 Drive API 权限范围,和你之前用的 SCOPES 保持一致
      authorization: {
        params: {
          scope: "openid email profile https://www.googleapis.com/auth/drive.readonly", // 替换成你的实际SCOPES
          access_type: "offline", // 可选,获取refresh token用于自动刷新
          prompt: "consent"
        }
      }
    })
  ],
  // 配置session策略为jwt,方便在会话中存储token
  session: {
    strategy: "jwt"
  },
  callbacks: {
    async jwt({ token, account }) {
      // 首次登录时保存access token、refresh token和过期时间
      if (account) {
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        token.expiresAt = account.expires_at * 1000; // 转成毫秒格式
      }
      // token未过期直接返回,过期则尝试刷新
      if (Date.now() < token.expiresAt) {
        return token;
      }
      return refreshAccessToken(token);
    },
    async session({ session, token }) {
      // 将access token注入session,供前端获取
      session.user.accessToken = token.accessToken;
      return session;
    }
  }
});

// 可选:实现access token自动刷新逻辑
async function refreshAccessToken(token) {
  try {
    const url =
      "https://oauth2.googleapis.com/token?" +
      new URLSearchParams({
        client_id: process.env.GOOGLE_CLIENT_ID,
        client_secret: process.env.GOOGLE_CLIENT_SECRET,
        grant_type: "refresh_token",
        refresh_token: token.refreshToken
      });

    const response = await fetch(url, {
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
      method: "POST"
    });

    const refreshedTokens = await response.json();

    if (!response.ok) throw refreshedTokens;

    return {
      ...token,
      accessToken: refreshedTokens.access_token,
      expiresAt: Date.now() + refreshedTokens.expires_in * 1000
    };
  } catch (error) {
    return { ...token, error: "RefreshAccessTokenError" };
  }
}

第二步:修改前端 gapi 初始化逻辑,复用 next-auth 的 Access Token

现在你不需要让 gapi 自己处理登录状态了,而是用 next-auth 提供的 access token 给 gapi 授权。修改你原来的 initClient 函数:

import { useSession } from "next-auth/react";
import { useEffect, useState } from "react";

const API_KEY = process.env.NEXT_PUBLIC_GOOGLE_API_KEY;
const DISCOVERY_DOCS = ["https://www.googleapis.com/discovery/v1/apis/drive/v3/rest"];

const DriveComponent = () => {
  const { data: session, status } = useSession();
  const [isLoadingGoogleDriveApi, setIsLoadingGoogleDriveApi] = useState(false);

  const initClient = async () => {
    if (!session?.user?.accessToken) return; // 无有效token时跳过初始化

    setIsLoadingGoogleDriveApi(true);
    try {
      // 加载gapi的客户端和auth模块
      await gapi.load("client:auth2");
      
      // 初始化gapi客户端,仅需apiKey和discoveryDocs(认证逻辑交给next-auth)
      await gapi.client.init({
        apiKey: API_KEY,
        discoveryDocs: DISCOVERY_DOCS
      });

      // 将next-auth的access token注入gapi的认证系统
      gapi.auth.setToken({ access_token: session.user.accessToken });

      // 测试调用Drive API(示例:获取文件列表)
      const response = await gapi.client.drive.files.list({
        pageSize: 10,
        fields: "nextPageToken, files(id, name)"
      });
      console.log("Drive文件列表:", response.result.files);
    } catch (error) {
      console.error("Gapi初始化失败:", error);
    } finally {
      setIsLoadingGoogleDriveApi(false);
    }
  };

  // 监听会话状态变化,登录成功后自动初始化gapi
  useEffect(() => {
    if (status === "authenticated") {
      initClient();
    }
  }, [status, session]);

  return (
    <div>
      {isLoadingGoogleDriveApi ? (
        <p>加载Google Drive API中...</p>
      ) : session?.user ? (
        <p>已登录,可操作Drive API</p>
      ) : (
        <p>请先登录</p>
      )}
    </div>
  );
};

export default DriveComponent;

关键注意点

  • 权限一致性:必须保证 NextAuth 中请求的 scope 和你之前单独使用 gapi 时的 SCOPES 完全一致,否则会出现权限不足的错误。
  • 自动刷新:NextAuth 配置中的 token 刷新逻辑会自动处理过期问题,当 token 刷新后,前端会话会更新,useEffect 会重新触发 initClient 更新 gapi 的授权状态。
  • 避免重复认证:现在 gapi 不再弹出自己的登录窗口,完全复用 next-auth 的会话状态,用户体验更统一。

如果遇到具体的报错(比如 token 无效、API 调用失败),可以把错误信息贴出来,我再帮你针对性排查!

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 21:12:45