基于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
相关产品推荐
相关产品推荐

