Bug report
Description
Logging out of a Catalyst storefront using Makeswift can result in an infinite redirect loop between:
I reproduced this issue using the base code from:
@bigcommerce/catalyst-makeswift@latest
I confirmed the redirect loop exists before making any application-specific changes.
The issue appears to be caused by patchSessionTokenCookies() re-setting the Auth.js session token without preserving the Partitioned and SameSite=None attributes configured for the Makeswift Auth.js session cookie.
This results in two logically distinct __Secure-authjs.session-token cookies being stored by the browser. During logout, Auth.js deletes the partitioned cookie it manages, but the additional unpartitioned session cookie can remain, causing the application to continue treating the customer as authenticated.
Environment
- BigCommerce Catalyst
- Makeswift integration
- Base code:
@bigcommerce/catalyst-makeswift@latest
- Production deployment: Vercel
- Authentication: Auth.js
- Browser: Chrome
The issue was reproduced from the base Catalyst Makeswift code before applying the proposed fix.
Steps to reproduce
- Deploy a storefront based on
@bigcommerce/catalyst-makeswift@latest to Vercel.
- Log into a customer account.
- Navigate to an authenticated account page such as
/account/orders.
- Inspect the site's cookies in Chrome DevTools.
- Observe the Auth.js
__Secure-authjs.session-token cookie.
- Interact with/navigate around the authenticated storefront.
- Two
__Secure-authjs.session-token cookies can be present with different cookie attributes:
SameSite=None, Partitioned
SameSite=Lax, non-partitioned
- Click Logout.
- Observe the application repeatedly redirect between
/login and /account/orders.
Manually deleting the surviving __Secure-authjs.session-token cookie stops the redirect loop.
Expected behavior
Logging out should:
- Invalidate the BigCommerce customer session.
- Delete the Auth.js customer session token.
- Establish an anonymous session as needed for cart persistence.
- Redirect the customer to
/login.
No authenticated Auth.js session cookie should remain after logout.
Actual behavior
After logout, one __Secure-authjs.session-token cookie can remain.
The application therefore continues to detect the customer as authenticated.
When /login checks the authentication state, the customer is redirected back to /account/orders. The account authentication flow then redirects back toward /login, resulting in a redirect loop.
Root cause
The Makeswift Auth.js configuration defines the session token as a partitioned cookie:
function partitionedCookie() {
return {
options: {
partitioned: true,
secure: true,
sameSite: 'none',
},
};
}
and configures Auth.js to use it:
cookies: {
sessionToken: partitionedCookie(),
callbackUrl: partitionedCookie(),
csrfToken: partitionedCookie(),
// ...
},
Therefore, Auth.js creates the customer session token with attributes equivalent to:
__Secure-authjs.session-token
Secure
SameSite=None
Partitioned=true
However, patchSessionTokenCookies() subsequently re-sets session-token cookies using:
cookieJar.set(name, value, {
httpOnly: true,
sameSite: 'lax' as const,
path: '/',
secure: name.startsWith('__Secure-'),
});
This does not preserve partitioned: true and changes SameSite from None to Lax.
The re-set cookie therefore has attributes equivalent to:
__Secure-authjs.session-token
Secure
SameSite=Lax
Partitioned=false
Partitioned and non-partitioned cookies are distinct browser cookies even when they have the same name and path.
As a result, the browser can contain both:
__Secure-authjs.session-token
SameSite=None
Partitioned=true
and:
__Secure-authjs.session-token
SameSite=Lax
Partitioned=false
During logout, Auth.js deletes the partitioned session cookie corresponding to its configured cookie.
The additional unpartitioned SameSite=Lax session token can remain.
The surviving token still contains the authenticated customer session/customer access token, causing the application to continue treating the customer as logged in and resulting in the /login ↔ /account/orders redirect loop.
Proposed fix
patchSessionTokenCookies() should preserve the cookie attributes used by the Auth.js Makeswift configuration when re-setting the session token without an Expires attribute.
Changing:
cookieJar.set(name, value, {
httpOnly: true,
sameSite: 'lax' as const,
path: '/',
secure: name.startsWith('__Secure-'),
});
to:
cookieJar.set(name, value, {
httpOnly: true,
secure: true,
sameSite: 'none',
partitioned: true,
path: '/',
});
resolves the issue.
For example:
async function patchSessionTokenCookies() {
const cookieJar = await cookies();
cookieJar.getAll().forEach(({ name, value }) => {
if (SESSION_TOKEN_NAME_RE.test(name) && value) {
cookieJar.set(name, value, {
httpOnly: true,
secure: true,
sameSite: 'none',
partitioned: true,
path: '/',
});
}
});
}
This continues to satisfy the purpose of patchSessionTokenCookies()—re-setting the session token without an Expires attribute so it remains a browser-session cookie—while preserving the cookie's partitioning semantics.
Verification
I verified the fix using a branch representing the base code from:
@bigcommerce/catalyst-makeswift@latest
Before the change
With the original patchSessionTokenCookies() implementation:
Customer login succeeds.
Multiple __Secure-authjs.session-token cookies with different partitioning/SameSite attributes can be created.
Clicking Logout results in a redirect loop between /login and /account/orders.
Manually deleting the surviving session cookie stops the loop.
After the change
After changing only patchSessionTokenCookies() to preserve:
secure: true,
sameSite: 'none',
partitioned: true,
and deploying the change:
Customer login succeeds normally.
Clicking Logout immediately redirects to /login.
No intermediate redirect to /account/orders occurs.
No redirect loop occurs.
The Auth.js customer session cookie is correctly deleted.
The successful logout response includes:
Set-Cookie: __Secure-authjs.session-token=;
Path=/;
Max-Age=0;
Secure;
HttpOnly;
SameSite=none;
Partitioned
The anonymous session is then established normally:
Set-Cookie: __Secure-authjs.anonymous-session-token=...;
Path=/;
Secure;
HttpOnly;
SameSite=lax
Possible regression history
patchSessionTokenCookies() appears to have been introduced in Catalyst 1.8.0 by:
fix(core): TRAC-814 Make session token cookies expire with the session (#3047)
The purpose of that change was to ensure authjs.session-token and authjs.anonymous-session-token behave as browser-session cookies without an Expires attribute.
When patchSessionTokenCookies() was introduced, the Auth.js configuration in that diff contained:
cookies: {},
and the helper re-set session cookies as SameSite=Lax without Partitioned.
Therefore, #3047 may not itself have introduced the logout bug.
The incompatibility appears to occur because the Makeswift Auth.js configuration now uses:
sessionToken: partitionedCookie()
with:
partitioned: true,
secure: true,
sameSite: 'none',
while patchSessionTokenCookies() continues to re-set that same cookie using the older unpartitioned SameSite=Lax attributes.
This suggests the regression may have been introduced when the Auth.js session cookie was changed to use partitionedCookie() without updating patchSessionTokenCookies() to preserve those attributes.
Additional notes
The issue is production-specific in the tested environment because production uses the secure Auth.js cookie:
__Secure-authjs.session-token
and Makeswift's partitioned cookie configuration.
The proposed change was tested on Vercel against the base Catalyst Makeswift code and consistently eliminated the logout redirect loop.
Bug report
Description
Logging out of a Catalyst storefront using Makeswift can result in an infinite redirect loop between:
/login/account/ordersI reproduced this issue using the base code from:
@bigcommerce/catalyst-makeswift@latestI confirmed the redirect loop exists before making any application-specific changes.
The issue appears to be caused by
patchSessionTokenCookies()re-setting the Auth.js session token without preserving thePartitionedandSameSite=Noneattributes configured for the Makeswift Auth.js session cookie.This results in two logically distinct
__Secure-authjs.session-tokencookies being stored by the browser. During logout, Auth.js deletes the partitioned cookie it manages, but the additional unpartitioned session cookie can remain, causing the application to continue treating the customer as authenticated.Environment
@bigcommerce/catalyst-makeswift@latestThe issue was reproduced from the base Catalyst Makeswift code before applying the proposed fix.
Steps to reproduce
@bigcommerce/catalyst-makeswift@latestto Vercel./account/orders.__Secure-authjs.session-tokencookie.__Secure-authjs.session-tokencookies can be present with different cookie attributes:SameSite=None,PartitionedSameSite=Lax, non-partitioned/loginand/account/orders.Manually deleting the surviving
__Secure-authjs.session-tokencookie stops the redirect loop.Expected behavior
Logging out should:
/login.No authenticated Auth.js session cookie should remain after logout.
Actual behavior
After logout, one
__Secure-authjs.session-tokencookie can remain.The application therefore continues to detect the customer as authenticated.
When
/loginchecks the authentication state, the customer is redirected back to/account/orders. The account authentication flow then redirects back toward/login, resulting in a redirect loop.Root cause
The Makeswift Auth.js configuration defines the session token as a partitioned cookie: