Skip to content

Fix: Add cookiePath config option for multi-shop non-embedded apps#3106

Open
byrichardpowell wants to merge 1 commit intomainfrom
worktree-cookie-path-config
Open

Fix: Add cookiePath config option for multi-shop non-embedded apps#3106
byrichardpowell wants to merge 1 commit intomainfrom
worktree-cookie-path-config

Conversation

@byrichardpowell
Copy link
Contributor

WHY are these changes introduced?

Fixes #451
Related: Shopify/shopify-app-template-react-router#156

Non-embedded apps that support multiple shops simultaneously in separate browser tabs hit a cookie collision: all shops share a single shopify_app_session cookie scoped to path=/, so each OAuth callback silently overwrites the previous shop's session. The user in Tab A then unknowingly operates on Tab B's shop after Tab B completes auth.

This is a known limitation acknowledged by paulomarg in issue #451 (Sept 2023) and has since resurfaced in the React Router template repo.

WHAT is this pull request doing?

Adds an optional cookiePath config option to ConfigParams (in @shopify/shopify-api) that controls the path attribute written on the shopify_app_session cookie during OAuth callback.

Before: hardcoded path: '/' — domain-wide, one cookie shared across all shops.

After: configurable via a static string or a per-session factory function:

// Default — unchanged behaviour
shopifyApi({ cookiePath: '/' })

// Multi-shop apps: scope cookie to a shop-specific URL prefix
shopifyApi({
  cookiePath: (session) => `/shops/${session.shop}/`,
})

When each shop is served under a distinct URL prefix, the browser maintains one cookie per shop and delivers only the matching cookie per request — no collision.

Changes:

  • lib/base-types.ts — adds cookiePath?: string | ((session: Session) => string) to ConfigParams with full JSDoc
  • lib/auth/oauth/oauth.ts — resolves the configured path at callback time and passes it to setAndSign
  • Tests — three new test cases: default path /, static string path, factory function path
  • Changeset — minor bump for @shopify/shopify-api

Note: this fix requires apps to structure their routing with shop-scoped URL prefixes. The library cannot derive the path automatically (see investigation notes on the chicken-and-egg read-time problem). The JSDoc explains this requirement.

Type of change

  • Patch: Bug (non-breaking change which fixes an issue)
  • Minor: New feature (non-breaking change which adds functionality)
  • Major: Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist

  • I have used pnpm changeset to create a draft changelog entry (do NOT update the CHANGELOG.md files manually)
  • I have added/updated tests for this change
  • I have documented new APIs/updated the documentation for modified APIs (for public APIs)

Non-embedded apps sharing a single `shopify_app_session` cookie at
path=/ experience silent session collisions when multiple shops are
authenticated in separate browser tabs — each OAuth callback overwrites
the previous shop's cookie.

The new optional `cookiePath: string | ((session) => string)` config
option lets apps scope the session cookie to a shop-specific URL prefix
so each shop's cookie coexists independently in the browser.

Fixes #451
Related: Shopify/shopify-app-template-react-router#156

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@byrichardpowell byrichardpowell changed the title Add cookiePath config option for multi-shop non-embedded apps Fix: Add cookiePath config option for multi-shop non-embedded apps Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Session cookie race condition with multiple shops

1 participant