Файл cookie токена обновления NextJS не синхронизируется с браузеромJavascript

Форум по Javascript
Ответить
Anonymous
 Файл cookie токена обновления NextJS не синхронизируется с браузером

Сообщение Anonymous »

У меня уже давно такая проблема. Я использую NextJS 16 для создания полностекового приложения с отдельным сервером NestJS. Проблема в том, что когда я выполняю логику обновления, файлы cookie httpOnly, которые я получаю обратно (содержащие новый токен доступа и токен обновления), не обновляются на территории клиента (браузере), и я понимаю, почему это кажется проблемой из-за другого жизненного цикла. Я пытаюсь не выполнять логику обновления в proxy.ts (предыдущий middleware.ts) и пытаюсь выполнить логику обновления, когда серверный компонент уже визуализируется (используя предварительную выборку запроса Tanstack React). Проблема по-прежнему сохраняется, поскольку не существует единственного хорошего способа синхронизации новых файлов cookie httpOnly, которые я получаю, чтобы браузер мог обновлять их. Вот реализации. Надеюсь получить некоторую информацию!
Примечание. Я использую «axios-auth-refresh» для обработки одновременных попыток обновления.
// Function to prefetch
const prefetchMe = async () => {
const queryClient = getQueryClient();
void queryClient.prefetchQuery({
queryKey: queryAuthKeys.me(),
queryFn: getMeServer,
staleTime: Infinity,
});
};

// The queryFn
const getMeServer = async (
context: QueryFunctionContext,
): Promise => {
const { signal } = context;
const { endpoint, method } = ENDPOINTS.IDENTITY_RELATED.protected.me;
const api = await getServerAuthApi();
const res = await api.request({
url: endpoint,
method,
signal,
});

return res.data;
};

// The axios instance used for server calls
const getServerAuthApi = cache(async () => {
const cookieStore = await cookies();
const apiInstance = axios.create({
baseURL,
});

// Create request interceptor
apiInstance.interceptors.request.use((config) => {
// Check if the Refresh Interceptor has already injected a new cookie
// into the defaults, if so, use it and BAIL OUT
const memoryCookie = apiInstance.defaults.headers.Cookie;

if (memoryCookie) {
config.headers.Cookie = memoryCookie;
return config;
}

// Fallback to initial setup
// Get both tokens
const accessToken = cookieStore.get(ACCESS_TOKEN_COOKIE_NAMESPACE)?.value;
const refreshToken = cookieStore.get(REFRESH_TOKEN_COOKIE_NAMESPACE)?.value;

// Build the Cookie header string
const cookieHeader = [
accessToken ? `${ACCESS_TOKEN_COOKIE_NAMESPACE}=${accessToken}` : "",
refreshToken ? `${REFRESH_TOKEN_COOKIE_NAMESPACE}=${refreshToken}` : "",
]
.filter(Boolean)
.join("; ");

if (cookieHeader) {
config.headers.Cookie = cookieHeader;
}
return config;
});

createAuthRefreshInterceptor(
apiInstance,
async (failedRequest) => {
const baseAppUrl = process.env.NEXT_PUBLIC_APP_URL;

// Calling my own api so that setting cookie is possible
const internalRes = await axios.request({
baseURL: baseAppUrl,
url: "/api/auth/refresh",
method: "POST",
headers: { Cookie: cookieStore.toString() },
});

const setCookieHeader = internalRes.headers["set-cookie"];
if (setCookieHeader) {
const parsedCookies = parse(setCookieHeader);
const newCookieString = parsedCookies
.map((c) => `${c.name}=${c.value}`)
.join("; ");

// This ensures headers are fresh for failed request (the first one)
if (failedRequest.headers) {
failedRequest.headers.Cookie = newCookieString;
}

// This ensures subsequent concurrect request(s) get a rresh cookie header
apiInstance.defaults.headers.Cookie = newCookieString;
}

return Promise.resolve();
},
{
statusCodes: [401],
pauseInstanceWhileRefreshing: true,
},
);

return apiInstance;
});

// /api/auth/refresh route for calling my backend refresh endpoint (needed so that cookies can be updated)
async function POST() {
const cookieStore = await cookies();
const refreshToken = cookieStore.get(REFRESH_TOKEN_COOKIE_NAMESPACE)?.value;

if (!refreshToken)
throw new Error("Aborting refresh, no refresh token available.");

try {
const { endpoint, method } = ENDPOINTS.IDENTITY_RELATED.public.refresh;
const baseURL = process.env.NEXT_PUBLIC_API_URL;

const refreshRes = await axios.request({
baseURL,
url: endpoint,
method,
headers: { Cookie: `${REFRESH_TOKEN_COOKIE_NAMESPACE}=${refreshToken};` },
});

const setCookies = refreshRes.headers["set-cookie"];

if (setCookies) {
const parsedCookies = parse(setCookies);

// Set new cookies
parsedCookies.forEach((c) => {
cookieStore.set({
name: c.name,
value: c.value,
path: c.path || "/",
maxAge: c.maxAge,
httpOnly: c.httpOnly ?? true,
secure: c.secure || process.env.NODE_ENV === "production",
sameSite: c.sameSite as ResponseCookie["sameSite"],
});
});
}

return NextResponse.json({ success: true }, { status: 200 });
} catch (error) {
console.error("Refresh Route Error:", error);
return NextResponse.json({ success: false }, { status: 401 });
}
}


Подробнее здесь: https://stackoverflow.com/questions/798 ... th-browser
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Javascript»