Can't set cookies in server action
Unanswered
American posted this in #help-forum
AmericanOP
Hello
I have a server action which I call from a form with
The code looks like this:
I see the console.log when I submit the form. I have
I have a server action which I call from a form with
useFormState. In this server action I want to set a cookie, but it's not getting saved.The code looks like this:
cookies().set("foo", "bar", {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "lax",
});
console.log("foobar");I see the console.log when I submit the form. I have
"use server" at the top of the file. I'm not sure how to debug this. I'm on Next 14.1. Any suggetions? ðŸ™45 Replies
@American Hello
I have a server action which I call from a form with `useFormState`. In this server action I want to set a cookie, but it's not getting saved.
The code looks like this:
ts
cookies().set("foo", "bar", {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "lax",
});
console.log("foobar");
I see the console.log when I submit the form. I have `"use server"` at the top of the file. I'm not sure how to debug this. I'm on Next 14.1. Any suggetions? ðŸ™
do you see the cookie in the browser with developer tools?
@Ray do you see the cookie in the browser with developer tools?
AmericanOP
Nope, I don't. But I do see the one I set in my middleware
@American Nope, I don't. But I do see the one I set in my middleware
could you show the code of the server action?
AmericanOP
Here's the full action:
export async function loginEmailUser(_prevState: unknown, formData: FormData) {
try {
const formDataObj = Object.fromEntries(formData.entries());
const { email, password } = LoginUser.parse(formDataObj);
const key = await auth.useKey("email", email.toLowerCase(), password);
const session = await auth.createSession({
userId: key.userId,
attributes: {},
});
cookies().set("foo", "bar", {
httpOnly: true,
secure: env.NODE_ENV === "production",
sameSite: "lax",
});
console.log("foobar");
const authRequest = auth.handleRequest("POST", {
cookies,
headers,
});
authRequest.setSession(session);
await redirectAnyUser();
} catch (e) {
if (e instanceof PublicError) {
return {
error: e.message,
};
}
if (e instanceof ZodError) {
return {
error: e.issues[0]?.message ?? "Validation error",
};
}
if (e instanceof LuciaError) {
return {
error: "Invalid email or password",
};
}
throw e;
}
}I added the foo cookie because the auth one wasn't sticking either
oh you mean the auth one doesn't set the cookie also?
@Ray oh you mean the auth one doesn't set the cookie also?
AmericanOP
Yes, so I added this foo one for a minimal example
@Ray could you check the network request for the response header? look for any `Set-Cookie`
AmericanOP
Thanks, I'll give it a look!
@American Thanks, I'll give it a look!
could you try
return {} instead of await redirectAnyUser()?@Ray could you try `return {}` instead of `await redirectAnyUser()`?
AmericanOP
That worked, thank you!
So I can't use it with redirect because it's throwing an error, maybe?
@American So I can't use it with redirect because it's throwing an error, maybe?
it should work and you also have rethrow the error
how does
redirectAnyUser look like?AmericanOP
export const getRedirectUrl = async () => {
const session = await getUserSession();
if (!session) {
return "/login";
}
if (!session.user.emailVerified) {
return "/login/verify-email";
}
if (!session.user.onboarded) {
return "/onboarding";
}
switch (session.user.role) {
case "STUDENT":
return "/elev";
case "TEACHER":
case "ROOT":
return "/larare";
}
return "/";
};
export const redirectAnyUser = async () => {
const redirectUrl = await getRedirectUrl();
redirect(redirectUrl);
};@Ray could you check the network request for the response header? look for any `Set-Cookie`
AmericanOP
I reverted the
return {} and tried this now, I can only see my locale cookie from the middleware getting set. Might they interfere?@Ray maybe try `redirect('/')` and see if it work first
AmericanOP
same problem
When I do
return {} I see all expected cookies in the response header btw@American When I do `return {}` I see all expected cookies in the response header btw
so it works with
return {}?AmericanOP
Yes, well, for setting the cookie
But then I'm not redirected, but I could do that in the UI for now
but
redirect('/') should work too, maybe rename your middleware to _middleware.ts and try to login againAmericanOP
Yeah, it works when I rename the middleware
🤯
I'm doing this in it btw:
export function middleware(request: NextRequest) {
const hostname = request.headers.get("host")!.replace("www.", "");
const locale = domainTable[hostname] ?? defaultLocale;
const response = NextResponse.next();
response.cookies.set("locale", locale, {
sameSite: "strict",
});
return response;
}Tried removing sameSite now just to test, same outcome
@American I'm doing this in it btw:
ts
export function middleware(request: NextRequest) {
const hostname = request.headers.get("host")!.replace("www.", "");
const locale = domainTable[hostname] ?? defaultLocale;
const response = NextResponse.next();
response.cookies.set("locale", locale, {
sameSite: "strict",
});
return response;
}
try this
export function middleware(c: NextRequest) {
if (req.method === 'GET') {
const hostname = request.headers.get("host")!.replace("www.", "");
const locale = domainTable[hostname] ?? defaultLocale;
const response = NextResponse.next();
response.cookies.set("locale", locale, {
sameSite: "strict",
});
return response;
}
}AmericanOP
Sorry, same thing
Do you think I should open an issue?
@American Sorry, same thing
try add a matcher like this to middleware
export const config = {
matcher: [
/*
* Match all request paths except for the ones starting with:
* - api (API routes)
* - _next/static (static files)
* - _next/image (image optimization files)
* - favicon.ico (favicon file)
*/
"/((?!api|_next/static|_next/image|favicon.ico).*)",
],
};AmericanOP
No, same again. I had this before:
export const config = {
matcher: [
// Skip all internal paths (_next)
"/((?!_next).*)",
],
};@American No, same again. I had this before:
ts
export const config = {
matcher: [
// Skip all internal paths (_next)
"/((?!_next).*)",
],
};
try this
"/((?!api|_next/static|_next/image|favicon.ico).*)",AmericanOP
Same again
@American Same again
oh well, I just tested and it only work when I set the matcher like that
could you refresh the page and test again?
AmericanOP
Thanks for the help and your time, I think I'm just gonna do a workaround for now
How do you mean?
browser hard refresh
AmericanOP
Same, tried another browser as well
I'm just gonna redirect on the client for now, thanks you for the help and your time ðŸ™
@Ray oh well, I just tested and it only work when I set the matcher like that
AmericanOP
What next version are you btw?
AmericanOP
Thanks, just checking