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

NextAuth.js(v5):如何在每次JWT回调中获取有效的AccessToken?

解决Auth.js(v5)中AccessToken持续可用的问题

你不需要手动把AccessToken存入Cookie,Auth.js的JWT策略已经帮你把加密后的token存在安全的HttpOnly Cookie里了。问题出在你的jwt回调逻辑上——当前你只在用户登录(account存在)时才把access_token写入JWT,但后续触发jwt回调(比如session刷新、调用auth()获取会话)时,account会是undefined,这时候如果不保留原有token里的access_token,就会丢失。

修正后的核心回调代码

export const {
    handlers,
    auth,
    signIn,
    signOut
} = NextAuth({
    providers: [Spotify],
    debug: true,
    secret: process.env.AUTH_SECRET,
    session: {
        strategy: "jwt"
    },
    callbacks: {
        session: async ({ session, token }) => {
            if (session?.user && token.sub) {
                session.user.id = token.sub;
            }
            // 从持久化的JWT token中取出access_token返回给前端会话
            return {...session, access_token: token.access_token};
        },
        jwt: async ({ token, user, account }) => {
            // 用户首次登录时,写入用户ID、access_token及相关凭证
            if (user && account) {
                token.uid = user.id;
                token.access_token = account.access_token;
                // 同时存入refresh_token和过期时间,用于后续自动刷新
                if (account.refresh_token) {
                    token.refresh_token = account.refresh_token;
                    token.expires_at = account.expires_at;
                }
            }

            // 每次触发jwt回调时,保留原有token的所有字段(包括已存入的access_token)
            return token;
        },
    }
})

进阶处理:AccessToken过期自动刷新

如果你的access token有过期时间,建议在jwt回调里加入过期检查逻辑,自动用refresh token获取新的access token:

jwt: async ({ token, user, account }) => {
    if (user && account) {
        token.uid = user.id;
        token.access_token = account.access_token;
        token.refresh_token = account.refresh_token;
        token.expires_at = account.expires_at;
        return token;
    }

    // 未过期则直接返回原token
    if (Date.now() < token.expires_at * 1000) {
        return token;
    }

    // 已过期,调用身份提供商的刷新接口
    try {
        const response = await fetch("https://accounts.spotify.com/api/token", {
            method: "POST",
            headers: {
                "Content-Type": "application/x-www-form-urlencoded",
                Authorization: `Basic ${Buffer.from(`${process.env.SPOTIFY_CLIENT_ID}:${process.env.SPOTIFY_CLIENT_SECRET}`).toString("base64")}`
            },
            body: new URLSearchParams({
                grant_type: "refresh_token",
                refresh_token: token.refresh_token
            })
        });

        const refreshedTokens = await response.json();
        if (!response.ok) throw refreshedTokens;

        return {
            ...token,
            access_token: refreshedTokens.access_token,
            expires_at: Date.now() / 1000 + refreshedTokens.expires_in,
            // 如果返回新的refresh_token,替换旧的
            ...(refreshedTokens.refresh_token && { refresh_token: refreshedTokens.refresh_token })
        };
    } catch (error) {
        console.error("Refresh token失败", error);
        // 刷新失败时标记错误,可在session回调中提示用户重新登录
        return { ...token, error: "RefreshTokenError" };
    }
},

为什么不用自己存Cookie?

Auth.js的JWT策略默认会把token存在HttpOnly、Secure、SameSite=Lax的Cookie中,这种Cookie无法被前端JS读取,能有效防范XSS攻击,安全性远高于手动存储的Cookie,完全没必要自己额外处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 15:45:39