Session Sync
In a previous release, we improved our GraphQL Authentication implementation which added the ability for a shopper to login from their Catalyst/headless storefront and remain logged in as they proceed into the Stencil checkout page.
This document outlines step-by-step instructions to further improve that feature which allow the shopper to login (and logout) from the Stencil checkout page, and have their login state persist back to the Catalyst/headless storefront.
Intended Behavior
Login Flow
- The shopper navigates to Stencil Checkout as a guest.
- They click Sign In.
- They’re redirected to the login page on the Catalyst/headless storefront.
- After signing in, they’re redirected back to Stencil Checkout, now signed in.
Logout Flow
- The shopper navigates to Stencil Checkout while signed in.
- They click Sign Out.
- They’re redirected to the logout route on the Catalyst/headless storefront.
- After signing out, they’re redirected back to Stencil Checkout, now as a guest.
Setup Instructions
-
Use the latest Catalyst code.
Ensure you’ve merged the following pull requests:
-
Configure login and logout routes.
Send a request to GET a Channel Site to retrieve the
site_idfor your Catalyst/headless storefront.Send a request to PUT Routes to configure the login and logout routes.
-
Enable storefront redirection in checkout settings.
To enable storefront redirection in checkout settings, send a request to PUT Checkout Settings to update the
should_redirect_to_storefront_for_authsetting.
Troubleshooting session sync
When a shopper starts checkout, Catalyst calls the createCartRedirectUrls GraphQL mutation, which returns a redirectedCheckoutUrl of the form https://<your-domain>/session-sync?jwt=.... The /session-sync endpoint belongs to BigCommerce’s checkout system, not to your Next.js app: BigCommerce validates the short-lived JWT it issued, transfers the cart and customer session, and then forwards the shopper to checkout.
Because that endpoint sits on the boundary between your Catalyst app and BigCommerce’s checkout servers, a misconfiguration on either side surfaces as the same generic Invalid JWT token message or a 404 response. The following sections cover the failure modes in the order they’re worth ruling out.
After three consecutive requests with an invalid session-sync JWT, BigCommerce blocks the originating IP address for five minutes. Wait out the block before retesting, or your next attempt will fail regardless of the fix.
Edge runtime in a checkout or login token route
Symptom: Invalid JWT token, or a runtime crash immediately after the checkout redirect. Server logs show an exception from crypto or jose.
Cause: export const runtime = 'edge' is present in core/app/[locale]/(default)/checkout/route.ts or core/app/[locale]/(default)/(auth)/login/token/[token]/route.ts. These routes sign and verify JWTs with Node.js APIs that the edge runtime does not provide.
Fix: Remove the export const runtime = 'edge' declaration from those routes so they run in the default Node.js runtime. Also check any catch-all route or wrapper that applies the edge runtime more broadly, and search the whole core/app directory rather than only the login and checkout paths:
BIGCOMMERCE_STOREFRONT_TOKEN is not a storefront token
Symptom: GraphQL Storefront API calls return 401 before the shopper reaches checkout, or the storefront renders with missing data. Recent Catalyst versions log an explicit InvalidStorefrontTokenError.
Cause: BIGCOMMERCE_STOREFRONT_TOKEN holds an OAuth access token from an API account — an opaque string — instead of a GraphQL Storefront API token. The GraphQL Storefront API accepts only storefront tokens, which are JWTs made up of three base64url-encoded segments separated by periods (xxxxx.yyyyy.zzzzz).
Fix: Send a request to Create a private token with the Unauthenticated and Customer scopes, scoped to the channel your storefront uses. Set the returned token as BIGCOMMERCE_STOREFRONT_TOKEN and redeploy. For the full variable reference, including the API account scope required to mint the token, see Environment variables.
Channel ID mismatch between the environment and the domain configuration
Symptom: Invalid JWT token at /session-sync. The decoded JWT’s channel_id claim doesn’t match the channel that has your custom domain configured, and redirect_to points at a different domain than the one the shopper is on — for example, a production checkout URL while the request hits a sandbox subdomain.
Cause: BIGCOMMERCE_CHANNEL_ID points at one channel while the custom storefront domain is registered under another. BigCommerce issues the session-sync JWT for the channel that Catalyst names in the mutation, so if that channel’s checkout domain doesn’t match the host receiving the request, validation fails.
Fix:
- In the control panel, open Channel Manager and select the channel that has the custom domain configured. The channel ID appears in the page URL.
- Set
BIGCOMMERCE_CHANNEL_IDto that channel ID exactly. - Regenerate
BIGCOMMERCE_STOREFRONT_TOKENscoped to the same channel ID. - Redeploy.
Give each environment its own channel with its own BIGCOMMERCE_CHANNEL_ID and BIGCOMMERCE_STOREFRONT_TOKEN. Sharing a channel between a sandbox and a production storefront guarantees a domain mismatch at session sync.
Custom domain is not the primary storefront URL for the channel
Symptom: /session-sync returns a 404. The JWT’s redirect_to claim points at a domain other than the one the shopper is browsing.
Cause: The domain was added as an additional or redirect domain instead of the channel’s primary storefront URL, or it was added to a channel other than the one BIGCOMMERCE_CHANNEL_ID names.
Fix: In the control panel, go to Channel Manager → [your channel] → Domains and set the custom domain as the primary storefront URL. Confirm no other active channel claims the same domain. After saving, allow 10–15 minutes for the checkout system to pick up the change before you retest.
DNS is not fully propagated
Symptom: The subdomain resolves inconsistently, or the session-sync request returns a connection error or a generic CDN 404 that doesn’t come from BigCommerce’s checkout system.
Cause: Nameserver or record changes for the domain haven’t propagated to BigCommerce’s infrastructure yet.
Fix:
-
Confirm the domain resolves to the servers BigCommerce expects — typically the same Cloudflare addresses as your other BigCommerce-hosted storefront domains:
-
Check the record with a DNS propagation checker to confirm it’s visible globally. Full propagation can take up to 48 hours, depending on the registrar and the previous TTL.
-
Load the storefront homepage on the custom domain and confirm it renders. If the homepage is broken, session sync will fail too.
Domain verification is still pending
Symptom: The domain shows as pending or unverified under the channel’s domain settings, and the checkout system doesn’t recognize it.
Cause: BigCommerce verifies ownership of a custom domain before enabling it for checkout. The domain can be live in your Catalyst environment before that verification completes.
Fix: Publish the verification record shown in the control panel — usually a CNAME or TXT record — and wait for the status to change to active. Don’t test checkout until it does. For Native Hosting projects, catalyst domains add prints the records to publish and catalyst domains status reports the current state; see Custom Domains.
JWT expiry under slow redirect conditions
Symptom: Intermittent Invalid JWT token errors that correlate with slow networks or high server load, and that don’t reproduce consistently.
Cause: A session-sync JWT is valid for 30 seconds. If a slow redirect chain, middleware delay, or client-side navigation wrapper sits between Catalyst generating the URL and the browser reaching /session-sync, the token can expire before BigCommerce validates it.
Fix: Redirect to redirectedCheckoutUrl from the server in a single hop, immediately after the mutation returns. Don’t route the checkout redirect through client-side navigation logic, and check your middleware for anything that buffers or delays the redirect response. Request the URL as part of the checkout action rather than caching it — a cached redirectedCheckoutUrl is expired by the time a shopper uses it.
Decode the session-sync JWT
The session-sync JWT is a standard JWT: three base64url-encoded segments separated by periods. You only need to read its claims, so decode the payload — the second segment — without verifying the signature:
Inspect the following claims: