Fix 401 Unauthorized on cart AJAX endpoints during shopify theme dev#6839
Merged
Fix 401 Unauthorized on cart AJAX endpoints during shopify theme dev#6839
shopify theme dev#6839Conversation
Cart AJAX endpoints (/cart/add.js, /cart/update.js, /cart/change.js, /cart.js, /cart.json) return 401 when running `shopify theme dev` on CLI 3.89.0, but work correctly on 3.88.0. 5+ developers affected. Root cause: Commit dda10ad added `Authorization: Bearer <storefrontToken>` to all proxied requests. Cart endpoints authenticate via session cookies (_shopify_essential). When SFR's BearerTokenAuthorizationMiddleware sees the Authorization header, it selects token-based auth instead of cookie auth. The CLI's Storefront API token lacks cart scopes (write_global_api_universal_cart), so GlobalApi::Authorize.perform() fails with 401. Fix: Added needsBearerToken() helper that returns false for cart, checkout, and account paths (session-cookie auth), and true for CDN/asset paths (need Bearer token for theme preview rendering). Key decisions: - Denylist (exclude specific paths) over allowlist because the set of cookie-auth endpoints is small/stable while Bearer-needing paths are open-ended. An allowlist would risk silent rendering breakage. - Separate CART_SESSION_PATTERN from CART_PATTERN because routing and auth have different scopes (routing excludes GET /cart to render locally; auth excludes all cart paths from Bearer tokens). - Reuses CHECKOUT_PATTERN and ACCOUNT_PATTERN from routing since their routing and auth scopes currently align.
Contributor
Coverage report
Test suite run success3733 tests passing in 1438 suites. Report generated by 🧪jest coverage report action from ac00b75 |
Flatten describe nesting to comply with vitest/max-nested-describe (max: 2) and use index signature style per consistent-indexed-object-style rule.
karreiro
approved these changes
Feb 10, 2026
Contributor
karreiro
left a comment
There was a problem hiding this comment.
Thank you for this PR, @kdaviduik! It looks good to me and works as expected as well:
Great stuff!
Contributor
|
👋 Hey @Shopify/app-inner-loop, could you please take a look at this when you have a moment? PS: I've proposed this PR to prevent similar review requests in the future 🙇 |
Contributor
|
/snapit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


WHY are these changes introduced?
Closes https://github.com/Shopify/developer-tools-team/issues/1034.
Cart AJAX endpoints (
/cart/add.js,/cart/update.js,/cart/change.js,/cart.js,/cart.json) return 401 Unauthorized when runningshopify theme devon CLI 3.89.0, but work correctly on 3.88.0. 5+ developers affected with confirmed reproduction.WHAT is this pull request doing?
Conditionally omits the
Authorization: Bearer <storefrontToken>header on proxied requests to endpoints that use session-cookie auth (cart, checkout, account). CDN/asset paths continue receiving the Bearer token for theme preview rendering.Changes:
proxy.tsCART_SESSION_PATTERNconstant,needsBearerToken()helper, updated Authorization conditionalproxy.test.ts.changeset/fix-cart-ajax-401.md@shopify/themeKey design decisions:
Denylist over allowlist: The set of cookie-auth endpoints is small and stable (cart, checkout, account). The set of Bearer-needing endpoints is large and open-ended (CDN assets, vanity CDN rewrites,
.js.liquidfiles, etc.). An allowlist would risk silent rendering failures; a denylist's failure mode is a loud 401 — easy to detect.Separate
CART_SESSION_PATTERNfromCART_PATTERN:CART_PATTERN(/^\/cart\//) is for routing — it deliberately excludesGET /cart(the cart HTML page, which should render locally).CART_SESSION_PATTERN(/^\/cart(\/|\.js|\.json|$)/) is for auth — broader because ALL cart paths use session-cookie auth. Collapsing these would contaminate routing with auth concerns.Reuse
CHECKOUT_PATTERNandACCOUNT_PATTERN: Their routing and auth scopes currently align. The comment inneedsBearerToken()warns future editors that changes to those patterns should consider auth implications.Query string stripping:
needsBearerToken()strips query strings before regex matching to ensureACCOUNT_PATTERN's$anchor matches correctly (e.g.,/account?foo=bar).How to test your changes?
Automated:
Manual (end-to-end):
Note: I'm currently having 1Password issues so I'm locked out of the admin and haven't been able to manually test these myself
shopify theme devon a test storeMeasuring impact
How do we know this change was effective? Please choose one:
Checklist