i am trying to fake the refreshtoken flow with mock jwt in my nextjs 14 app router using api routes and a test component but its not getting complete, i want on 401 403 or after 10 seconds refreshing the jwt happens and backend give me a new accesstoken and refreshtoken once it does that old ones removes so the new ones set in cookie again and this flow must continue by this pattern:
once you want to log something this flow must be shown on the test page:
1- attempt to log in 
2- loggedin accesstoken and refreshtoken jwt decoded be shown
3- 401 error happens then refreshing the token happens and once it was done new jwt decoded be shown
4- 403 error happens then refreshing the token happens and once it was done new jwt decoded be shown
5- 10 mock delay happens after countdown refreshing the token happens and once it was done new jwt decoded be shown
import axios, {
  AxiosError,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from "axios";
import { deleteCookie, getCookie, setCookie } from "cookies-next";

const localUrls = [
  "/api/home",
  "/api/auth/login",
  "/api/auth/protected",
  "/api/auth/refresh",
];
const isLocalUrl = process.env.NEXT_PUBLIC_BASE_LOCAL_URL;
const baseURL = process.env.NEXT_PUBLIC_BASE_URL;

const Axios = axios.create({
  baseURL,
  headers: { "Content-Type": "application/json" },
  withCredentials: true,
});

let isRefreshing = false;
let failedQueue: Array<{
  resolve: (value?: unknown) => void;
  reject: (reason?: any) => void;
}> = [];

const processQueue = (
  error: AxiosError | null,
  token: string | null = null,
) => {
  failedQueue.forEach(({ resolve, reject }) =>
    error ? reject(error) : resolve(token),
  );
  failedQueue = [];
};

const setAuthHeader = (config: InternalAxiosRequestConfig) => {
  const token = getCookie("accessToken");
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
};

Axios.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    const isLocalRequest = localUrls.some((url) => config.url?.startsWith(url));
    config.baseURL = isLocalRequest ? isLocalUrl : baseURL;
    return setAuthHeader(config);
  },
  (error: AxiosError) => Promise.reject(error),
);

Axios.interceptors.response.use(
  (response: AxiosResponse) => response,
  async (error: AxiosError) => {
    const originalRequest = error.config as InternalAxiosRequestConfig & {
      _retry?: boolean;
    };

    if (
      (error.response?.status === 401 || error.response?.status === 403) &&
      !originalRequest._retry
    ) {
      if (isRefreshing) {
        return new Promise((resolve, reject) => {
          failedQueue.push({ resolve, reject });
        }).then((token) => {
          originalRequest.headers.Authorization = `Bearer ${token as string}`;
          return Axios(originalRequest);
        });
      }

      originalRequest._retry = true;
      isRefreshing = true;

      try {
        const refreshToken = getCookie("refreshToken");
        if (!refreshToken) {
          throw new Error("No refresh token available");
        }

        const response = await Axios.post("/api/auth/refresh", {
          refreshToken,
        });
        const { accessToken, refreshToken: newRefreshToken } = response.data;

        setCookie("accessToken", accessToken, { maxAge: 60 * 15 });
        setCookie("refreshToken", newRefreshToken, {
          maxAge: 60 * 60 * 24 * 7,
        });

        Axios.defaults.headers.common["Authorization"] =
          `Bearer ${accessToken}`;
        processQueue(null, accessToken);
        return Axios(originalRequest);
      } catch (refreshError) {
        processQueue(refreshError as AxiosError, null);
        deleteCookie("accessToken");
        deleteCookie("refreshToken");
        throw refreshError;
      } finally {
        isRefreshing = false;
      }
    }

    return Promise.reject(error);
  },
);

export default Axios;

"use client";
import Axios from "@/src/configs/axios";
import { useEffect, useState } from "react";
import { getCookie } from "cookies-next";
import { decodeJwt } from "jose";

const decodeJWT = (token: string) => {
  try {
    return JSON.stringify(decodeJwt(token), null, 2);
  } catch (error) {
    return "Invalid JWT";
  }
};

export default function TestRefresh() {
  const [logs, setLogs] = useState<string[]>([]);
  const [countdown, setCountdown] = useState<number | null>(null);

  const addLog = (message: string) => {
    setLogs((prev) => [...prev, message]);
  };

  const fakeLogin = async () => {
    addLog("1. Attempting to log in...");
    try {
      const response = await Axios.post("/api/auth/login", {
        username: "testuser",
        password: "testpassword",
      });
      const { accessToken, refreshToken } = response.data;
      addLog("2. Logged in successfully. Decoded tokens:");
      addLog(`Access Token: ${decodeJWT(accessToken)}`);
      addLog(`Refresh Token: ${decodeJWT(refreshToken)}`);
      return { accessToken, refreshToken };
    } catch (error) {
      addLog("Login failed");
      throw error;
    }
  };

  const makeProtectedRequest = async (errorCode?: number) => {
    try {
      if (errorCode) {
        // Simulate an error response
        throw { response: { status: errorCode } };
      }
      const response = await Axios.get("/api/auth/protected");
      addLog(`Protected request successful: ${JSON.stringify(response.data)}`);
    } catch (error: any) {
      if (error.response?.status === 401) {
        addLog("3. 401 error occurred. Refreshing token...");
      } else if (error.response?.status === 403) {
        addLog("4. 403 error occurred. Refreshing token...");
      } else {
        addLog(`Error in protected request: ${error.message}`);
      }
      throw error;
    }
  };

  const getAndLogNewTokens = () => {
    const newAccessToken = getCookie("accessToken") as string;
    const newRefreshToken = getCookie("refreshToken") as string;
    addLog(`New Access Token: ${decodeJWT(newAccessToken)}`);
    addLog(`New Refresh Token: ${decodeJWT(newRefreshToken)}`);
  };

  useEffect(() => {
    async function testRefreshFlow() {
      try {
        await fakeLogin();

        // Simulate 401 error
        try {
          await makeProtectedRequest(401);
        } catch (error) {
          addLog("New tokens after 401:");
          getAndLogNewTokens();
        }

        // Simulate 403 error
        try {
          await makeProtectedRequest(403);
        } catch (error) {
          addLog("New tokens after 403:");
          getAndLogNewTokens();
        }

        // Start countdown for mock delay
        addLog("5. Starting 10-second countdown for mock delay...");
        for (let i = 10; i > 0; i--) {
          setCountdown(i);
          await new Promise((resolve) => setTimeout(resolve, 1000));
        }
        setCountdown(null);

        addLog("Mock delay completed. Refreshing token...");
        try {
          await makeProtectedRequest();
        } catch (error) {
          addLog("New tokens after delay:");
          getAndLogNewTokens();
        }

        addLog("Refresh token flow test completed!");
      } catch (error: any) {
        addLog(`Error during test: ${error.message}`);
      }
    }

    testRefreshFlow();
  }, []);

  return (
    <div>
      <h1>Refresh Token Test</h1>
      {countdown !== null && <h2>Countdown: {countdown}</h2>}
      <div>
        {logs.map((log, index) => (
          <p key={index}>{log}</p>
        ))}
      </div>
    </div>
  );
}

import { NextRequest, NextResponse } from "next/server";
import { createJWT } from "@/src/helper/jwt";

export async function POST(request: NextRequest) {
  const { username, password } = await request.json();

  if (username === "testuser" && password === "testpassword") {
    const accessToken = await createJWT({ role: "user" }, "15m");
    const refreshToken = await createJWT({ role: "user" }, "7d");

    const response = NextResponse.json({ accessToken, refreshToken });

    response.cookies.set("accessToken", accessToken, {
      httpOnly: true,
      maxAge: 60 * 15, // 15 minutes
    });
    response.cookies.set("refreshToken", refreshToken, {
      httpOnly: true,
      maxAge: 60 * 60 * 24 * 7, // 7 days
    });

    return response;
  }

  return NextResponse.json({ error: "Invalid credentials" }, { status: 401 });
}

import { NextRequest, NextResponse } from "next/server";
import { verifyJWT } from "@/src/helper/jwt";

export async function GET(request: NextRequest) {
  const accessToken = request.cookies.get("accessToken")?.value;

  if (!accessToken) {
    return NextResponse.json(
      { error: "No access token provided" },
      { status: 401 },
    );
  }

  try {
    await verifyJWT(accessToken);
    return NextResponse.json({ message: "Protected data" }, { status: 200 });
  } catch (error) {
    return NextResponse.json(
      { error: "Invalid or expired token" },
      { status: 401 },
    );
  }
}


import { NextRequest, NextResponse } from "next/server";
import { createJWT, verifyJWT } from "@/src/helper/jwt";

export async function POST(request: NextRequest) {
  const { refreshToken } = await request.json();

  if (!refreshToken) {
    return NextResponse.json(
      { error: "No refresh token provided" },
      { status: 401 },
    );
  }

  try {
    await verifyJWT(refreshToken);

    const newAccessToken = await createJWT({ role: "user" }, "15m");
    const newRefreshToken = await createJWT({ role: "user" }, "7d");

    const response = NextResponse.json({
      accessToken: newAccessToken,
      refreshToken: newRefreshToken,
    });

    response.cookies.set("accessToken", newAccessToken, {
      httpOnly: true,
      maxAge: 60 * 15, // 15 minutes
    });
    response.cookies.set("refreshToken", newRefreshToken, {
      httpOnly: true,
      maxAge: 60 * 60 * 24 * 7, // 7 days
    });

    return response;
  } catch (error) {
    return NextResponse.json(
      { error: "Invalid refresh token" },
      { status: 401 },
    );
  }
}

import { JWTPayload, jwtVerify, SignJWT } from "jose";

const secret = new TextEncoder().encode(process.env.NEXT_PUBLIC_JWT_SECRET);

const generateRandomId = () => {
  return (
    Math.random().toString(36).substring(2, 15) +
    Math.random().toString(36).substring(2, 15)
  );
};

export const createJWT = async (payload: JWTPayload, expiresIn: string) => {
  const jwt = await new SignJWT(payload)
    .setProtectedHeader({ alg: "HS256" })
    .setIssuedAt()
    .setExpirationTime(expiresIn)
    .setJti(generateRandomId())
    .sign(secret);
  return jwt;
};

export const verifyJWT = async (token: string) => {
  try {
    const { payload } = await jwtVerify(token, secret);
    return payload;
  } catch (error) {
    throw new Error("Invalid or expired token");
  }
};
this is current wrong result:
Refresh Token Test
Countdown: 2
1. Attempting to log in...

1. Attempting to log in...

2. Logged in successfully. Decoded tokens:

Access Token: { "role": "user", "iat": 1728282282, "exp": 1728283182, "jti": "mz43p8wcfz93mk0urfzduw" }

Refresh Token: { "role": "user", "iat": 1728282282, "exp": 1728887082, "jti": "5j28wclu8og6hx5r9lyn8s" }

3. 401 error occurred. Refreshing token...

New tokens after 401:

New Access Token: Invalid JWT

New Refresh Token: Invalid JWT

4. 403 error occurred. Refreshing token...

New tokens after 403:

New Access Token: Invalid JWT

New Refresh Token: Invalid JWT

5. Starting 10-second countdown for mock delay...

2. Logged in successfully. Decoded tokens:

Access Token: { "role": "user", "iat": 1728282282, "exp": 1728283182, "jti": "ka6x9w9xi62nfm16x00bc" }

Refresh Token: { "role": "user", "iat": 1728282282, "exp": 1728887082, "jti": "4q69oc6wtenhmj7dk22jlb" }

3. 401 error occurred. Refreshing token...

New tokens after 401:

New Access Token: Invalid JWT

New Refresh Token: Invalid JWT

4. 403 error occurred. Refreshing token...

New tokens after 403:

New Access Token: Invalid JWT

New Refresh Token: Invalid JWT

5. Starting 10-second countdown for mock delay...