如何通过Next Auth获取Google access_token并调用YouTube API
解决NextAuth中Google Provider的access_token会话传递问题
一、修正NextAuth配置里的回调逻辑
你当前的jwt回调存在参数引用错误,正确的做法是直接从回调的account参数中获取access_token,而非从token.token.account读取。以下是修正后的完整配置:
import NextAuth from "next-auth"; import GoogleProvider from "next-auth/providers/google"; const options = { providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorization: { params: { scope: "openid email profile https://www.googleapis.com/auth/youtube.readonly", }, }, }), ], callbacks: { async jwt({ token, account }) { // 首次登录时,将谷歌返回的access_token存入jwt if (account) { token.accessToken = account.access_token; token.expiresAt = account.expires_at * 1000; // 转成毫秒便于过期判断 } return token; }, async session({ session, token }) { // 将jwt中的access_token传递到前端会话 if (token) { session.accessToken = token.accessToken; session.expiresAt = token.expiresAt; } return session; }, }, debug: true, }; export default (req, res) => NextAuth(req, res, options);
二、在客户端调用YouTube API
通过NextAuth的useSession钩子获取会话中的access_token,然后在请求头中携带该token调用YouTube API:
import { useSession } from "next-auth/react"; import { useEffect, useState } from "react"; const YouTubeChannelInfo = () => { const { data: session } = useSession(); const [channelInfo, setChannelInfo] = useState(null); useEffect(() => { const fetchChannelData = async () => { if (!session?.accessToken) return; try { const res = await fetch( "https://www.googleapis.com/youtube/v3/channels?part=snippet,statistics&mine=true", { headers: { Authorization: `Bearer ${session.accessToken}`, }, } ); const data = await res.json(); setChannelInfo(data.items[0]); } catch (err) { console.error("获取YouTube数据失败:", err); } }; fetchChannelData(); }, [session]); if (!channelInfo) return <div>加载中...</div>; return ( <div> <h3>你的YouTube频道</h3> <p>频道名称:{channelInfo.snippet.title}</p> <p>订阅数:{channelInfo.statistics.subscriberCount}</p> <img src={channelInfo.snippet.thumbnails.default.url} alt="频道头像" /> </div> ); }; export default YouTubeChannelInfo;
三、关于你的问题解答
是否可以将access_token加入会话?
完全可以,通过jwt和session回调的配合,就能实现将谷歌返回的access_token传递到前端会话中,上述代码已验证可行。是否应该让access_token在会话数据中可用?
如果需要在客户端直接调用YouTube API,那么是必要的。但需要注意:
- access_token属于敏感信息,必须在HTTPS环境下传输,避免在控制台打印或无意义的本地存储;
- access_token有过期时间,建议补充token自动刷新逻辑(参考下方扩展代码)。
扩展:token自动刷新逻辑
如果需要处理access_token过期问题,可以在jwt回调中加入刷新逻辑:
async jwt({ token, account, user }) { // 首次登录保存完整token信息 if (account && user) { return { accessToken: account.access_token, expiresAt: Date.now() + account.expires_in * 1000, refreshToken: account.refresh_token, user, }; } // token未过期则直接返回 if (Date.now() < token.expiresAt) { return token; } // 过期则调用谷歌接口刷新token return refreshAccessToken(token); } // 刷新access_token的工具函数 async function refreshAccessToken(token) { try { const res = await fetch("https://oauth2.googleapis.com/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: 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 refreshedTokens = await res.json(); if (!res.ok) throw refreshedTokens; return { ...token, accessToken: refreshedTokens.access_token, expiresAt: Date.now() + refreshedTokens.expires_in * 1000, refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, }; } catch (err) { console.error("Token刷新失败:", err); return { ...token, error: "RefreshAccessTokenError" }; } }
内容的提问来源于stack exchange,提问作者Connor Kirkland
相关产品推荐
相关产品推荐

