Next.js Discord

Discord Forum

How to update session's refresh and access token manually in server action file such as axio setup?

Unanswered
rand posted this in #help-forum
Open in Discord
Hello, folks, Im looking for a solution to implement token rotation while i call axios api call.
Here is my axios code but i dont think session.accessToken = responseOfTokenRefreshAPI.access works.

How to update the session data outside of authJS?
Thank you!

"use server";

import { auth } from "@/auth";
import axios from "axios";
import { signOut } from "next-auth/react";

const api = axios.create({
  baseURL: process.env.NEXT_PUBLIC_API_BASE_URL,
});

let isRefreshing = false; // Track if a token refresh is in progress
let refreshSubscribers: ((token: string) => void)[] = []; // Queue requests while refreshing

// Helper to subscribe to the token refresh
const subscribeTokenRefresh = (cb: (newToken: string) => void) => {
  refreshSubscribers.push(cb);
};

// Notify all subscribers with the new token
const onRefreshed = (newToken: string) => {
  refreshSubscribers.forEach((cb) => cb(newToken));
  refreshSubscribers = [];
};

// Request interceptor: Attach the access token
api.interceptors.request.use(async (config) => {
  const session = await auth();
  if (session?.accessToken) {
    config.headers.Authorization = `Bearer ${session.accessToken}`;
  }

  return config;
});

// Response interceptor: Handle token expiration and refresh logic
api.interceptors.response.use(
  (response) => response, // Return response if no error
  async (error) => {
    const originalRequest = error.config;

    // Handle 401 errors (Unauthorized)
    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;

      // Prevent multiple refresh requests at the same time
      if (!isRefreshing) {
        isRefreshing = true;

        try {
          // Refresh the token
          const session = await auth();
          if (!session?.refreshToken) {
            throw new Error("No refresh token available");
          }
          console.log("REFRESH TOKEN", session.refreshToken)
          const refreshResponse = await axios.post(
            `${process.env.NEXT_PUBLIC_API_BASE_URL}/api/auth/refresh/`,
            {
              refresh: session.refreshToken,
            }
          );

          console.log("REFRESH RESPONSE:", refreshResponse.data);

          const { access, refresh } = refreshResponse.data; // Extract the new access token

          // Update the session with the new access token
          session.accessToken = access;
          session.refreshToken = refresh;

          // Notify all queued requests with the new token
          onRefreshed(access);

          isRefreshing = false;

          // Retry the original request with the new access token
          return api(originalRequest);
        } catch (refreshError) {
          isRefreshing = false;
          console.error("Token refresh failed", refreshError);

          // If refresh fails, sign out the user
          signOut({ callbackUrl: "/login" });
          return Promise.reject(refreshError);
        }
      }

      // Queue the failed request while refreshing
      return new Promise((resolve) => {
        subscribeTokenRefresh((newToken: string) => {
          originalRequest.headers.Authorization = `Bearer ${newToken}`;
          resolve(api(originalRequest)); // Retry with the new token
        });
      });
    }

    return Promise.reject(error); // Return other errors as-is
  }
);

export default api;

0 Replies