I have this nextjs app where i'm trying to setup my authentication/authorization system, it's pretty simple, i have a refresh token (long duration) which i store in a httpOnly cookie, and i have also an access token (short duration) in them cookies (at first, i wanted to store my access token as a Bearer access-token
header, but, setting that header up in server components and the client side got me crying for almost a week since it is impossible).
So, if my access-token cookie expires, i need to call my api (this is where problem shows up) using this endpoint (/access-token
) that generates the new token, and sets the cookie, but, if i'm calling this endpoint in a server component the cookie won't be set in my browser, and i understand that since a server component is running on the server side and not on client side.
Then i said, aaight cool, i'ma set the cookie in my nextjs app
, so, when i call the same endpoint, i return the access token and set it using import {cookies} from 'next/headers'
, but it didn't work since server components can not have side effects for cache and design purposes (and things of that nature).
Then i go to the docs, where they say that if i want to set a cookie, i need a server action or a route handler, and this is what i did.
My server component (home page /) π
const fetchUser = async (): Promise<HomeData | undefined> => {
await getAccessToken("aaight");
// await axios.post(
// "http://localhost:3000/api/set-cookie",
// {},
// {
// headers: {
// Cookie: cookies().toString(),
// },
// timeout: 5000,
// }
// );
const refreshToken = cookies().get("refresh-token");
// console.log("refresh token", refreshToken);
if (!refreshToken) {
return undefined;
}
console.log("\ncookie set\n", cookies().getAll());
// get user
const getUser = await foodyApi.get<SuccessfullResponse<{ user: User }>>(
"/users/me",
{
headers: {
Cookie: cookies().toString(),
},
timeout: 10000,
}
);
return {
user: getUser.data.rsp.user,
};
};
Server action (/app/action.ts) π (this is the first fn that runs in my server component because i need to do this types of operations before streaming starts)
"use server";
import { cookies } from "next/headers";
export const getAccessToken = async (token: string) => {
cookies().set("access-token", token, {
path: "/",
domain: "localhost",
maxAge: 300,
httpOnly: true,
secure: false,
});
};
This also didn't work, and i got an error β that says.
`
β¨― Error: Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#cookiessetname-value-options
at getAccessToken (./app/action.ts:15:61)
at fetchUser (./app/page.tsx:20:66)
at Home (./app/page.tsx:51:24)
at async Promise.all (index 0)
`
Then i try with an api route handler π (/app/api/set-cookie)
import { cookies } from "next/headers";
export const POST = async (req: Request) => {
// FIXME: check we got refresh token and api key
console.log(cookies().get("access-token"));
cookies().set("access-token", "we did it");
return Response.json(
{},
{ status: 200 }
);
};
That didn't work as well.
It's been a week for me trying to setup a normal auth system for my app, and i've changed things about my system so that i can use nextjs, but i'm having problems again, how am i supposed to set my cookie now ?ΒΏ redirect to another page with use client
mark and set the cookie then redirect here to my page with the server component ?ΒΏ that work around sounds horrible, and i don't know about user experience man...