Reputation: 21
I am currently experiencing an issue with setting cookies in a Next.js 14 route handler. I have confirmed through console logs that the token is correctly included in the Set-Cookie header, but the cookie is not appearing in the browser. I have tried adjusting various options like SameSite, secure, and httpOnly, but nothing seems to work. What could be the problem? Below is my code:
// app/auth/login/page.tsx
import { redirect } from 'next/navigation';
import { LoginPage } from 'common';
interface Params {
searchParams?: { [key: string]: string | undefined };
}
const Login = async ({ searchParams }: Params) => {
const loginCode = searchParams?.code;
if (loginCode) {
const res = await fetch(
new URL(`/auth/login/post?code=${loginCode}`, process.env.BASE_URL),
{
method: 'POST',
}
);
console.log(res.headers.getSetCookie()); // (1)
redirect('/');
}
return <LoginPage />;
};
export default Login;
// app/auth/login/post/route.ts
import { cookies } from 'next/headers';
import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import type { TokenResponseLoginType } from 'types';
export async function POST(request: NextRequest) {
const cookieStore = cookies();
try {
.
.
.
const tokenData: TokenResponseLoginType = await res.json();
cookieStore.set('refresh_token', tokenData.refreshToken);
cookieStore.set('access_token', tokenData.accessToken);
// just try. (before -> return Response.redirect . . .)
return new Response('response', {
status: 200,
headers: {
'Set-Cookie': `refresh_token=${tokenData.refreshToken}; access_token=${tokenData.accessToken}`,
},
});
} catch (e) {
return NextResponse.redirect(new URL('/auth/login', request.url));
}
}
(1) ['access_token=blablabla; Path=/']
I feel like I've tried everything regarding cookies. I've adjusted the options when setting the cookies, and I've even switched browsers. Here are the methods I've attempted:
Setting the SameSite option to Strict, Lax, and None. Toggling the secure option on and off. Setting and unsetting the httpOnly option. Checking and adjusting the cookie domain and path. Confirming the Set-Cookie header is properly included in the server response through console logs. Testing across different browsers to see if the issue is browser-specific. Ensuring the server is running in the correct environment (development or production) to match the cookie settings.
Upvotes: 2
Views: 2268
Reputation: 521
Faced the same issue in setting cookies using a route handler in NextJS 14. Maybe its something to do with streaming
Tried a few options:
cookies()
from NextJS cookies - doesn't workSet cookie
header Set cookie header - doesn't seem to work (Need more testing)What works for me is the middleware method
but in a single route handler. Something similar to this
e.g.
export async function POST(request: NextRequest) {
// your code
....
const nextResponse = NextResponse.json(data, {
headers: request.headers,
});
nextResponse.cookies.set("<your-cookie-key>",<your-cookie-value>);
return nextResponse;
}
Upvotes: 0
Reputation: 11
you can use the route handlers , next js, can send headers , by writing new Headers(res.headers) it will include all the headers from the response and send it to you frontend.
import { cookies } from "next/headers";
import { NextRequest, NextResponse } from "next/server";
import { LoginSchema } from "~/schemas";
// Function to include cookies in request headers
const sendCookies = (options?: HeadersInit) => {
const cookieStore = cookies();
return {
Cookie: cookieStore.toString(),
...options,
};
};
// Handle GET requests
export async function GET(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const pathparts = pathname.split("/");
const type = pathparts[3];
try {
if (type === "session") {
const res = await fetch("http://localhost:3000/auth/api/session", {
method: "GET",
cache: "no-cache",
headers: sendCookies(),
});
const body = await res.json();
const headers = new Headers(res.headers);
const setCookieHeader = headers.get('Set-Cookie');
console.log(setCookieHeader);
return NextResponse.json(
{ user: body.user },
{
status: 200,
headers: {
'Set-Cookie': setCookieHeader || ''
},
},
);
}
} catch (error) {
if (error instanceof Error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
);
}
}
}
// Handle POST requests
export async function POST(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const pathparts = pathname.split("/");
const type = pathparts[3]; // e.g., "login"
if (type === "login") {
try {
const body = await request.json();
const validate = await LoginSchema.safeParseAsync(body);
if (!validate.success) {
return NextResponse.json(
{ error: validate.error.errors[0].message },
{ status: 403 }
);
}
const res = await fetch("http://localhost:3000/auth/login", {
method: "POST",
headers: sendCookies({
"Content-Type": "application/json",
}),
body: JSON.stringify(body), // Include the body for POST request
});
const headers = new Headers(res.headers); // it has the cookies from the response
const result = await res.json();
return NextResponse.json(result, {
status: 200,
headers,
});
} catch (error) {
if (error instanceof Error) {
return NextResponse.json(
{ error: error.message },
{ status: 500 }
);
}
}
}
}
Upvotes: 0
Reputation: 91
In order to set the cookies in api route in next js 14. You have to return the response that sets the cookies
//api/[myroute]/route.ts
const res = NextResponse.json({})
const newExpires = new Date(Date.now() + cookieValidTime)
res.cookies.set(
'session',
JSON.stringify({
...oldSession,
user: {
...oldSession.user,
...updatedFields,
expires: newExpires,
},
} as ISession),
{
expires: newExpires,
httpOnly: true,
}
)
return res
also, just in case the dynamic pages might be cached by next server you can set the dynamic page time to 0 second in next.config.mjs file
//next.config.mjs
experimental: {
staleTimes: {
dynamic: 0, // default is 30
static: 180,
},
},
Upvotes: 1