如何在Axios拦截器中刷新Next-Auth Token并更新会话数据?
解决Next-Auth在Axios拦截器中更新会话的可行方案
1. 先创建Next.js内部刷新接口 /api/auth/refresh
这个接口负责调用第三方认证服务刷新token,同时更新Next-Auth的会话数据:
// pages/api/auth/refresh.ts import { getSession, updateSession } from "next-auth/react"; import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { if (req.method !== "POST") { return res.status(405).json({ message: "仅支持POST请求" }); } try { const session = await getSession({ req }); if (!session?.tokens.refreshToken) { return res.status(401).json({ message: "无可用刷新令牌" }); } // 调用你的后端认证服务刷新token const refreshResponse = await fetch(`${process.env.API_URL}/auth/refresh`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ refreshToken: session.tokens.refreshToken }), }); if (!refreshResponse.ok) throw new Error("刷新令牌失败"); const newTokens = await refreshResponse.json(); // 更新Next-Auth会话,替换旧令牌 await updateSession({ ...session, tokens: { ...session.tokens, accessToken: newTokens.accessToken, // 如果后端返回新的refreshToken就替换,否则保留旧的 refreshToken: newTokens.refreshToken || session.tokens.refreshToken, }, }); res.status(200).json({ accessToken: newTokens.accessToken }); } catch (error) { // 刷新失败,清除会话并重定向到登录页(可选) await updateSession(null); res.status(401).json({ message: "刷新失败,请重新登录" }); } }
2. 修改Axios拦截器,处理401并更新会话
添加并发请求防重复刷新逻辑,确保同一时间只有一个刷新请求:
import axios from "axios"; import { getSession } from "next-auth/react"; const API_URL = process.env.NEXT_PUBLIC_API_URL; // 存储刷新请求的Promise,避免并发重复刷新 let refreshPromise: Promise<string | null> | null = null; export const ApiAuth = () => { const instance = axios.create({ baseURL: API_URL }); // 请求拦截器:携带当前会话的accessToken instance.interceptors.request.use(async (request) => { const session = await getSession(); if (session) { request.headers["Authorization"] = `Bearer ${session.tokens.accessToken}`; } return request; }); // 响应拦截器:处理401错误并刷新token instance.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; // 仅处理首次401错误,避免无限循环 if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; if (!refreshPromise) { refreshPromise = (async () => { try { // 调用内部刷新接口获取新token const refreshRes = await axios.post("/api/auth/refresh"); return refreshRes.data.accessToken; } catch (refreshError) { // 刷新失败,跳转到登录页 window.location.href = "/auth/login"; return null; } finally { refreshPromise = null; } })(); } const newAccessToken = await refreshPromise; if (newAccessToken) { // 更新原请求的Authorization头,并重试请求 originalRequest.headers["Authorization"] = `Bearer ${newAccessToken}`; return instance(originalRequest); } } // 其他错误直接抛出 return Promise.reject(error); } ); return instance; };
3. 确保Next-Auth会话包含tokens字段
在你的Next-Auth配置文件(pages/api/auth/[...nextauth].ts)中,通过回调将tokens注入到会话中:
import type { NextAuthOptions } from "next-auth"; import NextAuth from "next-auth"; export const authOptions: NextAuthOptions = { providers: [/* 你的认证提供商配置 */], session: { strategy: "jwt" }, // 用jwt策略更适合令牌管理 callbacks: { async jwt({ token, user }) { // 首次登录时保存用户的tokens if (user) token.tokens = user.tokens; return token; }, async session({ session, token }) { // 将tokens添加到前端可获取的session中 session.tokens = token.tokens; return session; }, }, }; export default NextAuth(authOptions);
内容的提问来源于stack exchange,提问作者getitdone
相关产品推荐
相关产品推荐

