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

NextAuth令牌轮换失效问题排查与替代方案咨询

NextAuth令牌轮换失效问题及自定义方案咨询

我们团队基于Next.js开发对接自研外部API的Web应用,采用NextAuth实现身份认证,目前遇到令牌轮换失效的问题:

  • 登录成功后,从后端获取有效期10分钟的Access Token和有效期更长的Refresh Token,将Access Token存入NextAuth会话,通过Axios发起API请求时携带该令牌。
  • 当Access Token过期时,通过Axios拦截器调用刷新接口获取新的Access Token和Refresh Token,接口调用成功,但NextAuth仍携带旧的过期令牌发起请求,导致请求失败。

这是我首次使用Next.js,团队目前考虑放弃NextAuth,改用自定义方案:将Refresh Token存入Redis并通过HttpOnly Cookie传递,SessionId存入本地存储实现持久登录,Access Token存入内存并在请求时携带,但不确定该方案是否可靠,希望得到协助。


相关代码

NextAuth配置代码

const authOptions: NextAuthOptions = {
  session: {
    strategy: "jwt",
  },

  providers:[
    CredentialsProvider({
        type: "credentials",
        credentials: {},

       async authorize(credentials, req){
            const { username, password } = credentials as {
                username: string;
                password: string;
              };

              if(!credentials){
                return null
              }
              const userData = {
                username: username,
                password: password
              }
            

              try {
                
                const response = await axios.post(`${BASE_URL}/login`, userData);
                if(response?.data){
                  
                  const user: jwtDecodedAttributes = jwt_decode(response?.data?.userToken);
                  
                  return {
                    id: user.userId,
                    name: user.username,
                    role: user.role,
                    profilepicture: user.profilepicture,
                    iat: user?.iat,
                    exp: user?.exp,
                    username: user?.username,
                    token: response?.data?.userToken,
                    email: user.email,
                    userId: user?.userId,
                    refresh: response?.data.userRefreshToken
                  };
            }
              
                
              } catch (error) {
                
                
              }
              return null
        }
    })
  ],

  pages: {
    signIn: "/login",
  },

callbacks:{
 async jwt({token, user, trigger, session}){
  if (trigger === "update") {
    return { ...token, ...session.user };
  }

      return {...token, ...user} ;
  },

 async session({session, token, user}){
    
    session.user = token
   
    
    return session
 }
}
};


 export default NextAuth(authOptions);

刷新令牌函数

export const RefreshFunction = ()=>{
         const { status, data, update } = useSession();



    const refreshToken = async()=>{
  const getItem = localStorage.getItem('refresh');
  const refreshData = {
    refresh: getItem,
   
  }
   
   if(data?.user.refresh){
    try {
      const res = await axios.post<axiosGetRefreshTokenAttributes>(`${BASE_URL}/refresh`, refreshData);

      const accessToken = res?.data?.userToken
      const refreshToken = res?.data?.refreshToken
      localStorage.removeItem('refresh');
     
      if(data?.user){
        await update({
          ...data,
          user:{
            ...data?.user,
            token: accessToken,
            refresh: refreshToken

          }
          
        })
       
        
        
      }
      localStorage.setItem('refresh', refreshToken as string)
     
    } catch (error) {
      return error
    }
   }

  }

   return refreshToken
   };

Axios拦截器函数

const useAxiosPrivate = () =>{
   const { status, data } = useSession();
   const refreshToken = RefreshFunction()



useEffect(() =>{
    const requestInterceptors = axiosPrivate.interceptors.request.use(
        config =>{
            if(!config.headers['authorization']){
                config.headers['authorization'] = `Bearer ${data?.user?.token}`
            }
            return config;
        }, (error) => Promise.reject(error)
    );

    const responseIntercept = axiosPrivate.interceptors.response.use(
        response => response,
        async (error)=>{
            const prevRequest = error?.config;
            console.log(error.response.status, 'kkk')
            if(error?.response.status === 401 && !prevRequest?.sent){
                prevRequest.sent = true;
               
                const token = await refreshToken()
                 prevRequest.headers['authorization'] = `Bearer ${token}`;
                
                 return axiosPrivate(prevRequest);
                
               
            }
            return Promise.reject(error);
        }
    );

    return()=>{
        axiosPrivate.interceptors.request.eject(requestInterceptors)
        axiosPrivate.interceptors.response.eject(responseIntercept)
    }

}, [data?.user?.token])

return axiosPrivate;
};


export default useAxiosPrivate;

API调用示例

const { status, data } = useSession();

 await axiosPrivate.post(`/createcompany`, companyData,  
 {withCredentials: true, headers: {Authorization: ` 
 ${data?.user.token}`}});

补充说明:如前所述,刷新接口会在令牌过期时被调用并生成新令牌,但新令牌无法更新到NextAuth会话,报错显示Access Token已过期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 06:07:04