
At first, I wanted to implement refresh token flow in my website. But then I got stuck with how cookies work in Nextjs 15. I was using cookies to store my JWT for API authorization.
When the access token expires, I fetch the refresh token to get a new one. The request is successful, but... I can't update the accessToken cookie from the client. Why? Because the cookie only exists on the server😐
For context:
I used custom fetch function (I used to use Axios before). When I tried to implement refresh token logic inside in an Axios interceptor, it caused waterfall looping. Axios kept hitting the refresh token endpoint over and over again because the cookie never got updated, and the new token never got set.

You can see in the image what flow I want implement to refresh the token. I know you can use Server Action or Route Handler but it is not worth the cookie still send the message
Yes, I know I could use Server Actions or Route Handlers, but it's still not working because... yeah "cookie only available on the server" error keeps showing up.
I tried two approaches:
/api/auth/configlogoutAction()async function satellite(path: string, options: RequestInit) {Add commentMore actions
const token = await getCookie("accessToken");
options.headers = {
...options.headers,
Authorization: `Bearer ${token}`,
};
let response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}${path}`,
options
);
if (response.status === 401) {
//WITH ROUTE HANDLER
try {
await fetch(`http://localhost:3000/api/auth/config`, {
method: "GET",
});
const getToken = await getCookie("accessToken");
options.headers = {
...options.headers,
Authorization: `Bearer ${getToken}`,
};
response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}${path}`,
options
);
} catch (error) {
throw error;
}
//WITH SERVER ACTION
logoutAction()
}
return response.json();
}
export default satellite;/api/auth/config
export async function GET(request: NextRequest) {
try {
const cookieStore = await cookies();
const token = cookieStore.get("refreshToken");
const response = await fetch(
`${process.env.NEXT_PUBLIC_API_URL}/auth/refresh-token`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
refreshToken: token?.value,
}),
}
);
if (!response.ok) {
cookieStore.delete("accessToken");
cookieStore.delete("refreshToken");
return NextResponse.redirect(new URL("/login", request.url));
}
const data = await response.json();
const accessToken = data.data.accessToken;
cookieStore.set("accessToken", accessToken, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
});
return new Response("success", {
status: 200,
});
} catch (error) {
console.log("[ERROR]: ", error);
return new Response(JSON.stringify({ error: "Failed to refresh token" }), {
status: 401,
headers: {
"Content-Type": "application/json",
},
});
}
}this server action I use
"use server"
export const logoutAction = async () => {
await deleteCookie("accessToken");
await deleteCookie("refreshToken");
redirect("/login");
};I didn't use both at the same time, just one or the other.
But the issue is I think the problem is when fetch in the response error. Next JS recognizing that fetch is not in the server again but it have on the browser client. so i can't access cookie in the server.
Because of all that mess, I decided no refresh token for now. Instead, I just use one token (access token), and to handle problem when token expired with status 401. I redirect to invalid-token route which is fake route.
Then in my middleware, I listen to that fake route and clear the cookie there. and yeaaah that work for me. yuhuuu finally
queryClient.prefetchQuery({
queryKey: ["user", user.data?.id || "me"],
queryFn: () => getMe(),
});
if (!user.success) {
if (user.status === 401) {
redirect("invalid-token");
}
return NotFound();
}this for the middleware
export async function middleware(request: NextRequest) {
const path = request.nextUrl.pathname;
if (path.startsWith("/invalid-token")) {
await deleteCookie("accessToken"); // calling server action
return NextResponse.redirect(new URL("/login", request.nextUrl));
}
}Credit for this solution here
If you know a proper way to implement refresh token with cookies in Nextjs 15, feel free to comment on my social media, DM, or emal me. Would love to hear your thoughts!✨✨