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

