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

如何通过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;

三、关于你的问题解答

  1. 是否可以将access_token加入会话?
    完全可以,通过jwt和session回调的配合,就能实现将谷歌返回的access_token传递到前端会话中,上述代码已验证可行。

  2. 是否应该让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 14:44:58