DEV Community

Cover image for BigCommerce Multi-Storefront vs Separate Stores: Session Isolation With BitBrowser MCP
yooexo
yooexo

Posted on

BigCommerce Multi-Storefront vs Separate Stores: Session Isolation With BitBrowser MCP

BigCommerce Multi-Storefront (MSF) runs several storefronts from one store: one control panel, one catalog, one list of staff users, and a separate domain for each storefront. Separate stores are the opposite arrangement. Each one is its own subscription with its own store hash, API credentials, staff list and apps.

Both setups are common, and both end up with the same practical problem for developers, QA people and agencies. You need to be signed in to several things at once (an admin, a test shopper on the German storefront, a different test shopper on the US one) without one session overwriting another or leaking into the next test.

This post maps where sessions live in each setup, works out how many isolated browser identities each one needs, and then builds and audits those identities with BitBrowser and its MCP server, so an AI client can create, open and organise them from plain prompts. There are two short Python scripts along the way: one reads your storefronts from the BigCommerce API, the other checks every profile for cookies that belong to a different store.

Diagram comparing one BigCommerce store with several storefronts and several separate stores, with the sessions each one needs

Table of contents

  1. Two ways to run several storefronts
  2. What Multi-Storefront shares and what it splits
  3. What separate stores give you
  4. Where sessions collide
  5. How many browser profiles each setup needs
  6. Connect BitBrowser MCP
  7. Pull the storefront list from the API
  8. Build the profiles with prompts
  9. Audit every profile for cookie bleed
  10. Test the customer login rules
  11. Mobile checks on cloud phones
  12. Multi-Storefront or separate stores
  13. FAQ

Two ways to run several storefronts

BigCommerce launched Multi-Storefront on 29 March 2022. Before that, a merchant with two brands or three countries usually ran two or three stores and logged in to each one separately.

With MSF, every extra storefront is a channel inside the same store. You add them in Channel Manager by buying storefront seats, and each seat gives you one active and one inactive storefront. The default storefront counts toward that. Only the store owner can buy seats, and the number you can buy is capped by your subscription plan, so check your plan before you design around ten storefronts.

Separate stores need nothing special. A store owner can open several stores on one account from the Account Dashboard, all billed to the same details, and the dashboard signs in with the same credentials as the stores. Each store still runs as its own instance.

Under the hood the difference shows up in the API. An MSF store has one store hash and many channels: channel 1 is the default storefront and can't be deleted, and every Stencil storefront has a site with its own public URL. Separate stores have one store hash each, and every integration has to be set up once per store.

What Multi-Storefront shares and what it splits

BigCommerce's own comparison of single and multi-storefront setups sorts settings into three groups.

Group Examples
Always per storefront domain name, storefront status, the message shown when a storefront is inactive
Global by default, can be overridden per storefront currencies, checkout, SEO, search engine robots, logo and favicon, customer privacy, order notifications, abandoned cart emails
Managed per storefront in Channel Manager themes, script manager, web pages, social links, home page carousel

Products are shared, but a product has to be assigned to a channel to appear on that storefront. Shipping methods get a Storefronts tab so you can switch each method on or off per storefront. There are limits too, for example up to 25,000 products per storefront.

Two shared things matter most for session isolation.

Staff permissions are global. BigCommerce says user permissions are shared across all storefronts, and you can't set them for one storefront. A staff member who can edit orders can edit orders from every storefront in the store.

Customer accounts can be global or channel-specific. Every customer record has a channel_ids field. When it is null, the customer can sign in to any channel whose allow_global_logins setting is true. When it holds a list such as [2], the customer can sign in only to those channels, whatever the global setting says. New customers start as null, and the default storefront allows global logins out of the box. So in a fresh MSF store, one shopper account works on every storefront unless you change it.

What separate stores give you

Separate stores give you hard walls. Each store has its own staff list, so a contractor who works on your outlet store doesn't see the main brand's orders. Each store has its own API accounts, so a leaked token for one brand can't touch the other. Apps are installed per store, which is more setup work but also means one app's bug stays in one place.

You pay for those walls with repetition. Product data has to be kept in sync between stores by an integration or by hand. Reporting across brands means pulling from several APIs. Every theme change, every script and every app subscription happens once per store.

For session purposes, though, separate stores are simpler to reason about. Nothing is shared, so the only question is how many logins you need open at once.

Where sessions collide

There are three kinds of session in play, and they collide in different ways.

The admin session. In MSF there is one control panel, so one staff login covers every storefront. That is convenient for the merchant. It is a problem for an agency that wants a contractor to work on one storefront only, because global permissions make that impossible inside one store. With separate stores, the merchant can issue a different staff user per store, and an agency often ends up holding several BigCommerce logins of its own: its own user, plus users that clients created for it. A browser keeps one BigCommerce login at a time, so moving between them means signing out and in again.

The shopper session. A Stencil storefront tracks the shopper with a session cookie (SHOP_SESSION_TOKEN) on that storefront's domain. Catalyst storefronts keep the session in an Auth.js cookie instead, starting with authjs.anonymous-session-token before sign-in. Because each MSF storefront has its own domain, two storefronts in the same browser don't share a cart. They do share everything else in that browser, though: the same IP address, the same language headers, the same leftover cookies from last week's test.

The test identity. This is where single-browser testing falls apart. Say the US storefront also sells to Canada, and its Canadian dollar has Canada in the "Default to [currency] in these countries" list. BigCommerce doesn't spell out how it decides where a visitor is, but location usually comes from the IP address, so the reliable way to check that default is to browse through a Canadian proxy. To test that a channel-specific customer is refused on the US storefront, you need a clean session with no earlier login on that domain. Doing both in one browser means clearing cookies between every step and remembering which VPN location you left switched on.

Incognito windows help for one test. They don't help when you want to keep a German test shopper signed in all week while you work on the US checkout.

How many browser profiles each setup needs

Here is a practical count. A "profile" means one isolated browser identity: its own cookies, local storage and proxy.

Setup Profiles you need Why
MSF store, internal team 1 admin profile, plus 1 test profile per storefront and customer type one admin login covers everything; test shoppers need clean, located sessions
Separate stores, internal team 1 profile per store each profile holds that store's admin and its storefront
Agency with client-issued logins 1 profile per client user keeps each client's credentials and cookies apart

Take an MSF store with two storefronts (US and Germany), two customer types (global and channel-specific) and one extra check of the US storefront as seen from Canada. That is one admin profile plus five test profiles. For a merchant with three separate stores, it is three.

Connect BitBrowser MCP

The profile count gets annoying to manage by hand around the tenth profile. That is the job the MCP server takes over.

BitBrowser is the tool this guide uses because each profile already carries its own cookies, storage, fingerprint and proxy, and because from version 7.1.5 the desktop app ships an MCP server. Its tool groups cover profiles (create, update, open, close, delete, query), fingerprints, proxies (configure, update, check status), groups, tags, window positions and cloud phones. None of those tools act inside a web page: the agent manages the profiles, and you or your test scripts do the clicking.

To connect it:

  1. Open Settings, then Browser Settings, then Local API.
  2. Switch on Enable Authentication Control and copy the API token.
  3. Add the server to your MCP client.

With Claude Code:

claude mcp add --transport http bitbrowser http://127.0.0.1:54345/mcp \
  --header "x-api-key: YOUR_API_TOKEN"
Enter fullscreen mode Exit fullscreen mode

Cursor, Codex and OpenCode take the same address and header in their own config files. The server listens on your own machine. Keep the token out of prompts, screenshots and repositories, as BitBrowser's guide asks.

Pull the storefront list from the API

Typing storefront URLs into prompts invites typos. The Channels and Sites APIs already know them, so read them from there. You need an API account with read-only access to channels and sites.

"""List the storefront channels and site URLs of one BigCommerce store.

Prints a starter profiles.json: one entry per storefront, with profile_id left
empty so you can fill it in after BitBrowser MCP creates the profiles.
"""
import json
import os
from urllib.parse import urlparse

import httpx

STORE_HASH = os.environ["BC_STORE_HASH"]
TOKEN = os.environ["BC_TOKEN"]
API_ROOT = os.environ.get("BC_API_ROOT", "https://api.bigcommerce.com")
BASE = f"{API_ROOT}/stores/{STORE_HASH}/v3"
HEADERS = {"X-Auth-Token": TOKEN, "Accept": "application/json"}


def get_all(client, path):
    """Read every page of a v3 collection."""
    items, url = [], BASE + path
    while url:
        resp = client.get(url, headers=HEADERS)
        resp.raise_for_status()
        body = resp.json()
        items.extend(body.get("data", []))
        nxt = body.get("meta", {}).get("pagination", {}).get("links", {}).get("next")
        url = BASE + path.split("?")[0] + nxt if nxt else None
    return items


def main():
    with httpx.Client(timeout=30) as client:
        channels = {
            ch["id"]: ch
            for ch in get_all(client, "/channels")
            if ch.get("type") == "storefront"
        }
        sites = get_all(client, "/sites")

    rows = []
    for site in sites:
        channel = channels.get(site.get("channel_id"))
        if channel is None:
            continue
        rows.append({
            "label": f"{STORE_HASH}-ch{channel['id']}",
            "channel": channel.get("name"),
            "status": channel.get("status"),
            "hosts": [urlparse(site["url"]).hostname],
            "profile_id": "",
        })
    print(json.dumps(rows, indent=2))


if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Run it once per store hash. For an MSF store you get one row per storefront. For separate stores you get one file per store, which you can merge. Marketplace and POS channels are skipped because they have no site to browse.

Add the admin host to each row by hand if that profile will also hold the control panel login (for example store-abc123.mybigcommerce.com). The audit script later uses the hosts list to decide which cookies belong where. If your admins also preview storefronts from the admin profile, list those storefront hosts in the admin entry too, or the audit will report them.

Build the profiles with prompts

Start the session with a ground rule, then build. These prompts are for the Acme example above: an MSF store with a US and a German storefront.

Before you create, update or delete anything, show me the plan and wait for me to type "go".

Create a group named BC-Acme.

In BC-Acme, create these profiles, each with its own fingerprint:
acme-admin, acme-us-global, acme-us-channel, acme-us-from-ca, acme-de-global, acme-de-channel.

Create tags: bc-admin, bc-test, geo-us, geo-de, geo-ca.

Tag acme-admin with bc-admin and the other five with bc-test. Add geo-de to the two acme-de profiles, geo-ca to acme-us-from-ca and geo-us to acme-us-global and acme-us-channel.

Set a German proxy on the geo-de profiles, a Canadian proxy on the geo-ca profile and a US proxy on the geo-us profiles. Leave every other setting unchanged. Then check the proxy status of all five test profiles.

List every profile in BC-Acme as JSON with id, name and tags.
Enter fullscreen mode Exit fullscreen mode

The "leave every other setting unchanged" line comes from BitBrowser's own advice for partial updates. Without it, an agent may reset fields you didn't mention. If a change doesn't show up in the app, refresh and ask the agent to query the profile again before repeating the command.

Copy the IDs from the last answer into the profile_id fields of your profiles.json. Then sign in by hand: the admin profile gets your staff login, each test profile gets its test shopper. The sessions stay in the profile from then on.

For separate stores, the same pattern gets shorter: one group per merchant, one profile per store, and the admin and storefront hosts of that store in the same profiles.json entry.

Audit every profile for cookie bleed

Profiles keep sessions apart only as long as people use them correctly. The most common slip is opening a different storefront inside the wrong profile, say checking the US checkout from the German test profile "just for a second". From then on that profile carries US cookies, and the next German test isn't clean.

This script opens each profile through BitBrowser's Local API, attaches Playwright over CDP, reads every cookie, and flags the ones whose domain belongs to another entry in profiles.json. A cookie set on a parent domain that covers the profile's own host (like .acme-shop.example) is not flagged.

"""Check that each BitBrowser profile only holds cookies for its own stores.

Exit codes: 0 all clean, 1 at least one profile holds another store's cookies,
2 no leaks found but some profiles could not be checked.
Run it when nobody is working in the profiles: it opens and closes each one.
"""
import json
import os
import sys

import httpx
from playwright.sync_api import sync_playwright

API = os.environ.get("BITBROWSER_API", "http://127.0.0.1:54345")
TOKEN = os.environ.get("BITBROWSER_TOKEN")  # set it if authentication is on
HEADERS = {"x-api-key": TOKEN} if TOKEN else {}


def covers(cookie_domain, host):
    domain = cookie_domain.lstrip(".").lower()
    host = host.lower()
    return host == domain or host.endswith("." + domain)


def foreign_cookies(entry, entries, cookies):
    others = {h: e["label"] for e in entries if e is not entry for h in e["hosts"]}
    found = {}
    for cookie in cookies:
        if any(covers(cookie["domain"], h) for h in entry["hosts"]):
            continue
        for host, label in others.items():
            if covers(cookie["domain"], host):
                found.setdefault(label, set()).add(cookie["domain"])
    return found


def read_cookies(pw, client, profile_id):
    resp = client.post(f"{API}/browser/open", json={"id": profile_id}, headers=HEADERS)
    body = resp.json()
    if not body.get("success"):
        raise RuntimeError(body.get("msg", "open failed"))
    browser = pw.chromium.connect_over_cdp(body["data"]["ws"])
    try:
        return browser.contexts[0].cookies()
    finally:
        browser.close()
        client.post(f"{API}/browser/close", json={"id": profile_id}, headers=HEADERS)


def main(path):
    with open(path) as f:
        entries = json.load(f)
    leaks = skipped = 0
    with sync_playwright() as pw, httpx.Client(timeout=60) as client:
        for entry in entries:
            try:
                cookies = read_cookies(pw, client, entry["profile_id"])
            except Exception as exc:  # keep going, report at the end
                print(f"SKIP  {entry['label']}: {exc}")
                skipped += 1
                continue
            found = foreign_cookies(entry, entries, cookies)
            if not found:
                print(f"OK    {entry['label']}: {len(cookies)} cookies, none from other stores")
                continue
            leaks += 1
            for label, domains in sorted(found.items()):
                print(f"LEAK  {entry['label']}: cookies from {label} ({', '.join(sorted(domains))})")
    sys.exit(1 if leaks else 2 if skipped else 0)


if __name__ == "__main__":
    main(sys.argv[1] if len(sys.argv) > 1 else "profiles.json")
Enter fullscreen mode Exit fullscreen mode

A run against a small profiles.json prints something like this:

LEAK  acme-de-global: cookies from acme-us-global (us.acme-shop.example)
OK    acme-us-global: 9 cookies, none from other stores
SKIP  client-b-admin: profile not found
Enter fullscreen mode Exit fullscreen mode

When a profile leaks, the fix is boring and reliable: clear that profile's cookies for the foreign domain, or delete the profile and let the agent recreate it from the same prompt. The exit codes make the script easy to drop into a weekly scheduled job.

Both scripts were run against local stand-ins for the BigCommerce and BitBrowser APIs (with real Chromium instances holding test cookies), not against a live store, so try them on one profile before pointing them at a client's setup. If you work with attribution across many profiles, there is a longer write-up on cross-profile cookie bleed in multi-account attribution that covers the analytics side of the same problem.

Three BitBrowser profiles tiled side by side, each signed in to a different BigCommerce storefront

Test the customer login rules

With clean, located profiles, the MSF customer rules become a short test matrix.

Customer channel_ids US storefront (allows global logins) DE storefront (global logins off)
Global test shopper null can sign in refused
DE-only test shopper [3] refused can sign in
Explicit both [1, 3] can sign in can sign in

To switch global logins off for one storefront, call the channel settings endpoint:

curl -X PUT "https://api.bigcommerce.com/stores/$BC_STORE_HASH/v3/customers/settings/channels/3" \
  -H "X-Auth-Token: $BC_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"allow_global_logins": false}'
Enter fullscreen mode Exit fullscreen mode

Then let the agent lay out the windows so you can read both results at once:

Open acme-us-global and acme-de-global and place them side by side on my main screen.

Close every open profile tagged bc-test.
Enter fullscreen mode Exit fullscreen mode

Sign in with the same test shopper in both windows and compare. Each profile has its own cookies and proxy, so neither login touches the other, and any difference in currency or tax you see comes from the storefront settings and the location, not from a leftover session. Open acme-us-from-ca the same way to check the Canadian dollar default.

Channel IDs above are examples. Read yours from the starter profiles.json.

Mobile checks on cloud phones

Desktop profiles cover most storefront testing, but a mobile checkout behaves differently: smaller layouts, the system keyboard over form fields, and payment sheets that only appear on a real phone browser.

A cloud phone is a remote Android or iOS device you open from your desktop. When BitBrowser is already running your profiles, BitCloudPhone for Android and BitCloudPhone-iOS for iPhone are the natural pick, because the same MCP server that builds your profiles can start and stop the phones too:

Open the cloud phone named qa-android-de.

Shut down every running cloud phone and tell me which ones were on.
Enter fullscreen mode Exit fullscreen mode

Give each storefront market its own phone, the same way you gave it its own desktop profile, and the German mobile checkout stays separate from the US one. Shutting phones down at the end of a session is worth turning into habit, since a forgotten device keeps running.

Multi-Storefront or separate stores

If the brands share most of their catalog and one team runs them, MSF saves a lot of duplicated work. One product update reaches every storefront it is assigned to, one app install covers the store (if the app supports MSF), and reporting starts in one place. Your session setup is one admin profile and a set of test profiles.

Pick separate stores when the walls matter more than the shared work. That usually means different legal entities, different teams that shouldn't see each other's orders, or a contractor who must only touch one brand. Global staff permissions in MSF make that last case impossible inside one store. Your session setup is one profile per store, and there is less to audit.

A mixed setup is normal too. Plenty of merchants run their main brands in one MSF store and keep a wholesale or outlet operation in a store of its own. In that case, give each store its own BitBrowser group and run the cookie audit across all of them, since leaks between stores are the ones that hurt.

FAQ

Does BigCommerce Multi-Storefront share customer accounts between storefronts?
By default, yes. New customers have channel_ids set to null, which lets them sign in to every channel that allows global logins, and the default storefront allows them. Set allow_global_logins to false on a channel, or give customers a specific channel_ids list, to keep accounts apart.

Can I limit a staff user to one storefront in Multi-Storefront?
No. User permissions are global across all storefronts in an MSF store. If a person must only see one brand, that brand needs its own store.

Do two storefronts in one browser share a cart?
Not when they are on different domains, because the session cookie belongs to each storefront's domain. They still share the browser's IP address, language and any leftover cookies, which is what spoils location and login tests.

What does BitBrowser MCP actually do here?
It lets an AI client create, tag, group, open, close and arrange browser profiles and cloud phones, and set or check their proxies. It doesn't click inside pages or sign in for you.

Do I need a separate proxy for every profile?
Only for the profiles where location matters, such as currency and tax tests for a specific country. An admin profile can use your normal connection.

Which MCP clients work with BitBrowser?
BitBrowser's guide lists Claude Code, Codex, Cursor, OpenCode and several others, plus a general configuration for any client that supports custom MCP servers.


Disclosure: I do content work for BitBrowser and BitCloudPhone, and the BitBrowser link in this post is a referral link.

Top comments (0)