Next.js 14 App Router刷新Access Token时Cookie修改报错求助
后端通过JWT返回access token和refresh token,用户登录后存入Cookie。当access token过期尝试刷新时,触发错误:
Cookies can only be modified in a Server Action or Route Handler.
相关代码
refreshAccessToken函数
'use server' import {cookies} from "next/headers"; import {isTokenExpiringSoon} from "@/app/utils/utils"; import {modifiedCookie} from "@/app/_actions/modifiedCookie"; export async function refreshAccessToken(refreshToken) { console.log("Attempting to refresh access token..."); if (!refreshToken) { console.error('Refresh token not found'); throw new Error('Refresh token not found'); } const refreshResponse = await fetch(`http://localhost:8000/api/token/refresh/`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ refresh: refreshToken, }), }); if (!refreshResponse.ok) { console.error('Failed to refresh token:', refreshResponse.statusText); throw new Error('Failed to refresh token'); } console.log('working on it') const data = await refreshResponse.json(); const newAccessToken = data.access; console.log(`newAccessToken in refresh: ${newAccessToken}`) modifiedCookie(newAccessToken) console.log("New access token set:", newAccessToken); return newAccessToken; }
fetchUserData函数
export async function fetchUserData() { const cookieStore = cookies(); const accessToken = cookieStore.get('access_token')?.value; console.log("Current access token in fetch:", accessToken); if (isTokenExpiringSoon(accessToken)) { console.log("Access token is expiring soon, refreshing..."); try { const refreshToken = cookieStore.get('refresh_token')?.value; console.log("Refresh Token:", refreshToken); await refreshAccessToken(refreshToken); } catch (error) { console.error('Error refreshing access token:', error); throw new Error('Unable to refresh access token'); } } const newAccessToken = cookieStore.get('access_token')?.value; console.log("New access token to use:", newAccessToken); const response = await fetch(`http://localhost:8000/api/user/user-profile/`, { method: 'GET', headers: { 'Authorization': `Bearer ${newAccessToken}`, 'Content-Type': 'application/json', }, }); if (!response.ok) { console.error('Failed to fetch user profile:', response.statusText); throw new Error('Failed to fetch user profile'); } const user = await response.json(); console.log("Fetched user data:", user); return user; }
错误日志
Current access token in fetch: ey... Access token is expiring soon, refreshing... Refresh Token: ey... Attempting to refresh access token... working on it newAccessToken in refresh: ey... New access token set: ey... New access token to use: ey... tA [Error]: Cookies can only be modified in a Server Action or Route Handler. at Proxy.callable (/home/comiser/Desktop/tasteproject/frontend/tastecoffee/node_modules/next/dist/compiled/next-server/app-page.runtime.dev.js:36:13455) at modifiedCookie (webpack-internal:///(rsc)/./app/_actions/modifiedCookie.js:14:61) at refreshAccessToken (webpack-internal:///(rsc)/./app/_actions/actions.js:41:80) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async fetchUserData (webpack-internal:///(rsc)/./app/_actions/actions.js:54:13) at async RootLayout (webpack-internal:///(rsc)/./app/layout.js:38:16) Failed to fetch user profile: Unauthorized Error fetching user profile: Failed to fetch user profile
错误原因
fetchUserData未标记为Server Action:虽然refreshAccessToken加了'use server',但调用它的fetchUserData没有该标识,导致执行上下文不允许修改Cookie。- Cookie读取时机错误:
fetchUserData一开始就获取了cookieStore,刷新token后直接读取旧的cookieStore无法拿到新token。
解决方案
1. 给fetchUserData添加Server Action标识
在函数顶部加上'use server',确保整个函数运行在允许修改Cookie的Server Action上下文:
'use server' export async function fetchUserData() { // 原有代码 }
2. 刷新后重新获取Cookie
调用refreshAccessToken后,重新获取Cookie实例以拿到最新的access token:
// 原代码 await refreshAccessToken(refreshToken); // 修改为: await refreshAccessToken(refreshToken); const updatedCookieStore = cookies(); const newAccessToken = updatedCookieStore.get('access_token')?.value;
3. 确保modifiedCookie在Server上下文运行
如果modifiedCookie是单独文件,需在顶部添加'use server':
// modifiedCookie.js 'use server' import {cookies} from "next/headers"; export async function modifiedCookie(newAccessToken) { cookies().set('access_token', newAccessToken, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 60 * 15, path: '/' }); }
内容的提问来源于stack exchange,提问作者comiser
相关产品推荐
相关产品推荐

