У меня уже давно такая проблема. Я использую 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
Файл cookie токена обновления NextJS не синхронизируется с браузером ⇐ Javascript
Форум по Javascript
1771218429
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 });
}
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79890009/nextjs-refresh-token-cookie-not-syncing-with-browser[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия