set cookie manually in next.js server action

"use server";

import { auth } from "@/auth";
import { cookies } from "next/headers";

export async function signInAction({
email,
password,
}: {
email: string;
password: string;
}) {
const res = await auth.api.signInEmail({
body: {
email,
password,
},
asResponse: true,
});



const cookieStore = await cookies();
const sessionCookieFull = res.headers.get("set-cookie");
if (sessionCookieFull) {
const [sessionCookie, _maxAge, _path, _httpOnly, _sameSite] =
sessionCookieFull.split(";").map((part) => part.trim());

const [key, value] = sessionCookie.split("=");
const maxAge = _maxAge.split("=")[1];
const path = _path.split("=")[1];
const httpOnly = Boolean(_httpOnly.split("=")[1]);
const sameSite = _sameSite.split("=")[1];
console.log({ key, value, maxAge, path, httpOnly, sameSite });
cookieStore.delete(key);
cookieStore.set(key, value, {
maxAge: Number(maxAge),
path,
httpOnly: true,
...(sameSite === "Lax" ? { sameSite: "lax" } : {}),
});
}
}
"use server";

import { auth } from "@/auth";
import { cookies } from "next/headers";

export async function signInAction({
email,
password,
}: {
email: string;
password: string;
}) {
const res = await auth.api.signInEmail({
body: {
email,
password,
},
asResponse: true,
});



const cookieStore = await cookies();
const sessionCookieFull = res.headers.get("set-cookie");
if (sessionCookieFull) {
const [sessionCookie, _maxAge, _path, _httpOnly, _sameSite] =
sessionCookieFull.split(";").map((part) => part.trim());

const [key, value] = sessionCookie.split("=");
const maxAge = _maxAge.split("=")[1];
const path = _path.split("=")[1];
const httpOnly = Boolean(_httpOnly.split("=")[1]);
const sameSite = _sameSite.split("=")[1];
console.log({ key, value, maxAge, path, httpOnly, sameSite });
cookieStore.delete(key);
cookieStore.set(key, value, {
maxAge: Number(maxAge),
path,
httpOnly: true,
...(sameSite === "Lax" ? { sameSite: "lax" } : {}),
});
}
}
I know there is a plugin to do this for me but i wanted to see if i could do it manually, i compared it to the cookie when i use the client instance and it seems pretty similar but my session is still undefined do i have to use the plugin or am i missing something? i think i normalized enough to properly set the cookie
Solution:
okay figured it out I needed to use decodeURIComponent on the value...
Jump to solution
3 Replies
KiNFiSH
KiNFiSH2w ago
first you have handle multiple cookies from the Set-Cookie header (Better Auth sets both session_token and session_data cookies) and attributes as well may be i can put a draft here -
"use server";

import { auth } from "@/auth";
import { cookies } from "next/headers";

export async function signInAction({
email,
password,
}: {
email: string;
password: string;
}) {
const res = await auth.api.signInEmail({
body: {
email,
password,
},
asResponse: true,
});

const cookieStore = cookies();
const sessionCookieFull = res.headers.get("set-cookie");

if (sessionCookieFull) {

const cookies = sessionCookieFull.split(",").map(cookie => cookie.trim());

for (const cookie of cookies) {
const [sessionCookie, ...attributes] = cookie.split(";").map(part => part.trim());
const [key, value] = sessionCookie.split("=");

const cookieAttributes: Record<string, any> = {};
for (const attr of attributes) {
const [attrKey, attrValue] = attr.split("=").map(part => part.trim());
cookieAttributes[attrKey.toLowerCase()] = attrValue;
}


cookieStore.delete(key);
cookieStore.set(key, value, {
maxAge: Number(cookieAttributes["max-age"]),
path: cookieAttributes.path || "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: cookieAttributes.samesite?.toLowerCase() || "lax",
...(cookieAttributes.domain ? { domain: cookieAttributes.domain } : {}),
});
}
}
}
"use server";

import { auth } from "@/auth";
import { cookies } from "next/headers";

export async function signInAction({
email,
password,
}: {
email: string;
password: string;
}) {
const res = await auth.api.signInEmail({
body: {
email,
password,
},
asResponse: true,
});

const cookieStore = cookies();
const sessionCookieFull = res.headers.get("set-cookie");

if (sessionCookieFull) {

const cookies = sessionCookieFull.split(",").map(cookie => cookie.trim());

for (const cookie of cookies) {
const [sessionCookie, ...attributes] = cookie.split(";").map(part => part.trim());
const [key, value] = sessionCookie.split("=");

const cookieAttributes: Record<string, any> = {};
for (const attr of attributes) {
const [attrKey, attrValue] = attr.split("=").map(part => part.trim());
cookieAttributes[attrKey.toLowerCase()] = attrValue;
}


cookieStore.delete(key);
cookieStore.set(key, value, {
maxAge: Number(cookieAttributes["max-age"]),
path: cookieAttributes.path || "/",
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: cookieAttributes.samesite?.toLowerCase() || "lax",
...(cookieAttributes.domain ? { domain: cookieAttributes.domain } : {}),
});
}
}
}
KHRM
KHRMOP2w ago
gave it a shot same result
const cookies = sessionCookieFull.split(",").map((cookie) => cookie.trim());
console.log(cookies);

// [
// 'better-auth.session_token=c8QV9jsfHyXL...A%2BpEhALc%3D; Max-Age=604800; Path=/; HttpOnly; SameSite=Lax'
// ]
const cookies = sessionCookieFull.split(",").map((cookie) => cookie.trim());
console.log(cookies);

// [
// 'better-auth.session_token=c8QV9jsfHyXL...A%2BpEhALc%3D; Max-Age=604800; Path=/; HttpOnly; SameSite=Lax'
// ]
it seems to only return one cookie also
Solution
KHRM
KHRM2w ago
okay figured it out I needed to use decodeURIComponent on the value

Did you find this page helpful?