feat(last-login-method): beforeStoreCookie option for GDPR compliance (#5753)

This commit is contained in:
Maxwell
2026-07-10 01:02:04 +00:00
committed by GitHub
parent f59a0ee789
commit f23ce5012e
7 changed files with 948 additions and 5 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"better-auth": patch
---
feat(last-login-method): beforeStoreCookie option for GDPR compliance
+2
View File
@@ -46,4 +46,6 @@ encoredev
korthout
esac
elif
anthropics
nextjs
+1 -1
View File
@@ -118,7 +118,7 @@ export const auth = betterAuth({
```
<Callout>
Each hook (`before` / `after`) takes a single middleware function, not an array. To run logic for different endpoints, branch on `ctx.path` inside that single function.
Each hook (`before` / `after`) takes a single middleware function, not an array. To run logic for different endpoints, branch on `ctx.path` inside that single function.
</Callout>
## Ctx
+1
View File
@@ -152,6 +152,7 @@ export default defineNuxtConfig({
## Resources & examples
{/* cspell:disable */}
* [`nuxt-modules/better-auth`](https://github.com/nuxt-modules/better-auth)
* [`atinux/nuxthub-better-auth`](https://github.com/atinux/nuxthub-better-auth)
* [`leamsigc/nuxt-better-auth-drizzle`](https://github.com/leamsigc/nuxt-better-auth-drizzle)
@@ -255,6 +255,14 @@ export const auth = betterAuth({
return null
},
// GDPR compliance hook
beforeStoreCookie: async (ctx, lastUsedLoginMethod) => {
// Check if user has given consent for non-essential cookies
// Return false to prevent cookie storage
const hasConsent = await checkUserCookieConsent(ctx)
return hasConsent
},
// Schema customization (when storeInDatabase is true)
schema: {
user: {
@@ -282,6 +290,14 @@ export const auth = betterAuth({
* Whether to store the last login method in the database
* Default: `false`
* When enabled, adds a `lastLoginMethod` field to the user table
* To store the method only in the database (no cookie), combine with `beforeStoreCookie: () => false`:
```ts title="auth.ts"
lastLoginMethod({
storeInDatabase: true,
beforeStoreCookie: () => false, // never set the non-essential cookie
})
```
**customResolveMethod**: `(ctx: GenericEndpointContext) => string | null`
@@ -289,6 +305,32 @@ export const auth = betterAuth({
* Return `null` to use the default resolution logic
* Useful for custom OAuth providers or authentication flows
**beforeStoreCookie**: `(ctx: GenericEndpointContext, lastUsedLoginMethod: string) => Promise<boolean> | boolean`
* Hook function that runs before storing the last login method cookie
* Return `true` to allow the cookie to be set, `false` to prevent it
* Useful for GDPR compliance or other regulations where cookie storage requires user consent
* If the function throws an error, the cookie will not be set (error is logged but doesn't break authentication)
* **Example**: Check user consent before storing the cookie
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
lastLoginMethod({
beforeStoreCookie: async (ctx, lastUsedLoginMethod) => {
// Check if user has given consent for non-essential cookies
// This is important for GDPR compliance
const hasConsent = await checkUserCookieConsent(ctx)
return hasConsent
}
})
]
})
```
**schema**: `object`
* Customize database field names when `storeInDatabase` is enabled
@@ -364,6 +406,44 @@ export const authClient = createAuthClient({
})
```
## GDPR Compliance
The last login method cookie is considered a non-essential cookie under GDPR regulations.
To comply with GDPR and similar privacy laws, you should only store this cookie if the user has given explicit consent.
The `beforeStoreCookie` hook allows you to implement consent checks before storing the cookie:
```ts title="auth.ts"
import { betterAuth } from "better-auth"
import { lastLoginMethod } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
lastLoginMethod({
beforeStoreCookie: async (ctx, lastUsedLoginMethod) => {
// Example 1: Check consent from session or database
const session = await getSessionFromCtx(ctx)
if (session?.user) {
// custom function which hits your database to check if the user has given consent
const userConsent = await checkUserConsent(session.user.id)
return userConsent?.allowsNonEssentialCookies ?? false
}
// Example 2: Check consent from request headers (cookie banner)
// parseConsentCookie should return false/null/undefined when no consent is present
const consentCookie = ctx.request?.headers?.get("cookie")
const hasConsent = parseConsentCookie(consentCookie)
return !!hasConsent
}
})
]
})
```
<Callout type="warn">
When `beforeStoreCookie` returns `false` or throws an error, the cookie will not be set. Authentication will still succeed normally - only the cookie storage is prevented.
</Callout>
## Advanced Examples
### Custom Provider Tracking
@@ -39,6 +39,20 @@ export interface LastLoginMethodOptions {
* @default false
*/
storeInDatabase?: boolean | undefined;
/**
* A hook to run before the last login method is stored in the cookie.
* Useful if you are required to follow GDPR or other regulations to ensure that you're allowed to store the last login method in the cookie.
*
* @param ctx - The context from the hook
* @param lastUsedLoginMethod - The last login method
* @returns `true` to store the cookie, `false` to skip storing it (authentication continues either way)
*/
beforeStoreCookie?:
| ((
ctx: GenericEndpointContext,
lastUsedLoginMethod: string,
) => Promise<boolean> | boolean)
| undefined;
/**
* Custom schema for the plugin
* @default undefined
@@ -168,6 +182,28 @@ export const lastLoginMethod = <O extends LastLoginMethodOptions>(
httpOnly: false, // Override: plugin cookies are not httpOnly
};
let isPermitted = true;
if (config.beforeStoreCookie) {
try {
isPermitted = await config.beforeStoreCookie(
ctx,
lastUsedLoginMethod,
);
} catch (error) {
// If beforeStoreCookie throws an error, don't set the cookie
// Log the error but don't break the authentication flow
if (ctx.context.logger) {
ctx.context.logger.error?.(
"[LastLoginMethod] Error in beforeStoreCookie hook",
error,
);
}
isPermitted = false;
}
}
if (!isPermitted) return;
ctx.setCookie(
config.cookieName,
lastUsedLoginMethod,
@@ -703,6 +703,829 @@ describe("lastLoginMethod", async () => {
);
});
describe("beforeStoreCookie hook", () => {
it("should set cookie when beforeStoreCookie returns true", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => true,
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should NOT set cookie when beforeStoreCookie returns false", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => false,
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
});
it("should set cookie when beforeStoreCookie returns Promise<true>", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
return true;
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should NOT set cookie when beforeStoreCookie returns Promise<false>", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
return false;
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
});
it("should set cookie when beforeStoreCookie is undefined (default behavior)", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [lastLoginMethod()],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should conditionally set cookie based on login method", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: (_ctx, lastUsedLoginMethod) => {
// Only allow storing for email logins
return lastUsedLoginMethod === "email";
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Email login should set cookie
const emailHeaders = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(emailHeaders)(context);
},
},
);
const emailCookies = parseCookies(emailHeaders.get("cookie") || "");
expect(emailCookies.get("better-auth.last_used_login_method")).toBe(
"email",
);
// OAuth login should NOT set cookie
const oAuthHeaders = new Headers();
const signInRes = await client.signIn.social({
provider: "google",
callbackURL: "/callback",
fetchOptions: {
onSuccess: cookieSetter(oAuthHeaders),
},
});
const state =
new URL(signInRes.data!.url!).searchParams.get("state") || "";
const oauthHeaders = new Headers();
await client.$fetch("/callback/google", {
query: {
state,
code: "test",
},
headers: oAuthHeaders,
method: "GET",
onError(context) {
cookieSetter(oauthHeaders)(context as any);
},
});
const oauthCookies = parseCookies(oauthHeaders.get("cookie") || "");
expect(
oauthCookies.get("better-auth.last_used_login_method"),
).toBeUndefined();
});
it("should conditionally set cookie based on context properties", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: (ctx) => {
// Only allow storing if path contains "sign-in"
return ctx.path.includes("sign-in");
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Sign-in should set cookie
const signInHeaders = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(signInHeaders)(context);
},
},
);
const signInCookies = parseCookies(signInHeaders.get("cookie") || "");
expect(signInCookies.get("better-auth.last_used_login_method")).toBe(
"email",
);
});
it("should allow different behavior for different login methods", async () => {
const allowedMethods = new Set(["email", "google"]);
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: (_ctx, lastUsedLoginMethod) => {
return allowedMethods.has(lastUsedLoginMethod);
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Email login should set cookie
const emailHeaders = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(emailHeaders)(context);
},
},
);
const emailCookies = parseCookies(emailHeaders.get("cookie") || "");
expect(emailCookies.get("better-auth.last_used_login_method")).toBe(
"email",
);
// Google OAuth should set cookie
const oAuthHeaders = new Headers();
const signInRes = await client.signIn.social({
provider: "google",
callbackURL: "/callback",
fetchOptions: {
onSuccess: cookieSetter(oAuthHeaders),
},
});
const state =
new URL(signInRes.data!.url!).searchParams.get("state") || "";
const oauthHeaders = new Headers();
await client.$fetch("/callback/google", {
query: {
state,
code: "test",
},
headers: oAuthHeaders,
method: "GET",
onError(context) {
cookieSetter(oauthHeaders)(context as any);
const cookies = parseSetCookieHeader(
context.response.headers.get("set-cookie") || "",
);
const lastLoginMethodCookie = cookies.get(
"better-auth.last_used_login_method",
)?.value;
if (lastLoginMethodCookie) {
expect(lastLoginMethodCookie).toBe("google");
}
},
});
const oauthCookies = parseCookies(oauthHeaders.get("cookie") || "");
expect(oauthCookies.get("better-auth.last_used_login_method")).toBe(
"google",
);
});
it("should handle dynamic consent changes between logins", async () => {
let consentGiven = false;
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => consentGiven,
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// First login without consent - cookie should NOT be set
const headers1 = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers1)(context);
},
},
);
const cookies1 = parseCookies(headers1.get("cookie") || "");
expect(
cookies1.get("better-auth.last_used_login_method"),
).toBeUndefined();
// Simulate user giving consent
consentGiven = true;
// Second login with consent - cookie should be set
await client.signOut();
const headers2 = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers2)(context);
},
},
);
const cookies2 = parseCookies(headers2.get("cookie") || "");
expect(cookies2.get("better-auth.last_used_login_method")).toBe("email");
});
it("should handle falsy return values correctly", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => {
// Test various falsy values
return 0 as any; // TypeScript allows this, but runtime will treat as false
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
// 0 is falsy, so cookie should NOT be set
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
});
it("should handle truthy return values correctly", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => {
// Test truthy value
return 1 as any; // TypeScript allows this, runtime will treat as true
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
// 1 is truthy, so cookie should be set
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should receive correct context and lastUsedLoginMethod parameters", async () => {
let receivedContext: any = null;
let receivedMethod: string | null = null;
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: (ctx, lastUsedLoginMethod) => {
receivedContext = ctx;
receivedMethod = lastUsedLoginMethod;
return true;
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
expect(receivedContext).toBeDefined();
expect(receivedContext.path).toBe("/sign-in/email");
expect(receivedMethod).toBe("email");
});
it("should work correctly with sign-up flow", async () => {
const { client, cookieSetter } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: (_ctx, lastUsedLoginMethod) => {
// Only allow for sign-up
return lastUsedLoginMethod === "email";
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signUp.email(
{
email: "newuser@example.com",
password: "password123",
name: "New User",
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should handle async operations in beforeStoreCookie", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: async (_ctx, lastUsedLoginMethod) => {
// Simulate async GDPR check
await new Promise((resolve) => setTimeout(resolve, 50));
// Simulate checking user consent from database
return lastUsedLoginMethod === "email";
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBe("email");
});
it("should NOT set cookie when beforeStoreCookie throws an error", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: () => {
throw new Error("GDPR check failed");
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Error should be caught gracefully, authentication should succeed, but cookie should not be set
const headers = new Headers();
let responseHeaders: Headers | null = null;
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
responseHeaders = context.response.headers;
},
},
);
// Verify cookie is not set
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
// Also check response headers
if (responseHeaders) {
const setCookieHeader =
(responseHeaders as Headers).get("set-cookie") || "";
const setCookie = parseSetCookieHeader(setCookieHeader);
expect(
setCookie.get("better-auth.last_used_login_method"),
).toBeUndefined();
}
});
it("should NOT set cookie when beforeStoreCookie returns a rejected promise", async () => {
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: async () => {
await new Promise((resolve) => setTimeout(resolve, 10));
throw new Error("GDPR check failed");
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Error should be caught gracefully, authentication should succeed, but cookie should not be set
const headers = new Headers();
let responseHeaders: Headers | null = null;
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
responseHeaders = context.response.headers;
},
},
);
// Verify cookie is not set
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
// Also check response headers
if (responseHeaders) {
const setCookieHeader =
(responseHeaders as Headers).get("set-cookie") || "";
const setCookie = parseSetCookieHeader(setCookieHeader);
expect(
setCookie.get("better-auth.last_used_login_method"),
).toBeUndefined();
}
});
it("should handle complex GDPR consent logic", async () => {
const userConsents = new Map<string, boolean>();
userConsents.set("test-gdpr@example.com", true);
const { client, cookieSetter } = await getTestInstance(
{
plugins: [
lastLoginMethod({
beforeStoreCookie: async (ctx, lastUsedLoginMethod) => {
// Simulate checking user consent from database
// Since ctx.body might not be available in the hook context,
// we'll use a simpler approach: check based on login method
// In a real scenario, you'd look up the user's consent from the database
// based on the session or user ID from ctx
return lastUsedLoginMethod === "email";
},
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
// Create user with consent first
await client.signUp.email(
{
email: "test-gdpr@example.com",
password: "password123",
name: "Test GDPR User",
},
{ throw: true },
);
// User with consent - email login should work
const headers1 = new Headers();
await client.signIn.email(
{
email: "test-gdpr@example.com",
password: "password123",
},
{
onSuccess(context) {
cookieSetter(headers1)(context);
},
},
);
const cookies1 = parseCookies(headers1.get("cookie") || "");
expect(cookies1.get("better-auth.last_used_login_method")).toBe("email");
});
it("should work correctly with custom cookie name", async () => {
const customCookieName = "custom.last_login_method";
const { client, cookieSetter, testUser } = await getTestInstance(
{
plugins: [
lastLoginMethod({
cookieName: customCookieName,
beforeStoreCookie: () => true,
}),
],
},
{
clientOptions: {
plugins: [lastLoginMethodClient()],
},
},
);
const headers = new Headers();
await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{
onSuccess(context) {
cookieSetter(headers)(context);
},
},
);
const cookies = parseCookies(headers.get("cookie") || "");
expect(cookies.get(customCookieName)).toBe("email");
expect(cookies.get("better-auth.last_used_login_method")).toBeUndefined();
});
it("should combine beforeStoreCookie with storeInDatabase correctly", async () => {
const { client, auth } = await getTestInstance({
plugins: [
lastLoginMethod({
storeInDatabase: true,
beforeStoreCookie: (_ctx, lastUsedLoginMethod) => {
// Only store cookie for email, but database will store anyway
return lastUsedLoginMethod === "email";
},
}),
],
});
const data = await client.signIn.email(
{
email: testUser.email,
password: testUser.password,
},
{ throw: true },
);
const session = await auth.api.getSession({
headers: new Headers({
authorization: `Bearer ${data.token}`,
}),
});
// Database should still store even if cookie is blocked
expect(session?.user.lastLoginMethod).toBe("email");
});
});
/**
* @see https://github.com/better-auth/better-auth/issues/9276
*/
@@ -784,7 +1607,6 @@ describe("lastLoginMethod", async () => {
});
it("should handle multiple set-cookie headers correctly", async () => {
// Create a custom plugin that sets an additional cookie to simulate multiple Set-Cookie headers
const multiCookiePlugin = {
id: "multi-cookie-test",
hooks: {
@@ -798,7 +1620,6 @@ describe("lastLoginMethod", async () => {
ctx.context.responseHeaders?.getSetCookie?.() || [];
const sessionTokenName =
ctx.context.authCookies.sessionToken.name;
// If session token is being set, also set an additional cookie BEFORE checking
const hasSessionToken = setCookieHeaders.some((cookie) =>
cookie.includes(sessionTokenName),
);
@@ -835,12 +1656,10 @@ describe("lastLoginMethod", async () => {
onSuccess(context) {
cookieSetter(headers)(context);
// Verify that multiple cookies are present in the response
const setCookieHeaders =
context.response.headers.getSetCookie?.() || [];
expect(setCookieHeaders.length).toBeGreaterThan(1);
// Verify both cookies are present
const cookieStrings = setCookieHeaders.join(";");
expect(cookieStrings).toContain("additional-test-cookie=test-value");
expect(cookieStrings).toContain(