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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 02:12:43