You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A shopper's basket is addressed by its ID, and on Elastic Path that ID is the entire access boundary: anyone holding it can read and write that basket, because the credential needed to act on one can be minted by anyone from the store's public ID. So the ID is a key, not a label.
ADR-0003 concluded that keeping the ID in server custody is the whole defence. But the storefront still hands it to the browser — the session endpoint returns it, and a script on the page can read it and replay it later from anywhere, for as long as the basket lives.
This issue decides whether to close that, and it is a checkout decision rather than an identity one, which is why ADR-0003 did not reach it.
Why it is still open
epCartId is on SESSION_ALLOWLIST (auth/ep-plugin/auth-routes.ts:28), so /api/ep/get-session returns it to any script on the page. Two components fetch it via getCartIdFromSession() (cart/cart-session.ts:30):
EPCheckoutProvider (:238) — survives. It resolves the ID on mount and passes it to useCheckout({ cartId }), which drives all four checkout steps against /api/checkout/sessions/*. The browser holds the ID for the whole checkout.
ADR-0003 scoped checkout out deliberately, and #491 said so explicitly: /api/checkout/sessions/* "stays separate: it holds the merchant client_credentials secret and computes authoritative money, and merges into neither browser-facing route." The surviving exposure sits on the checkout side of that boundary, so it is a boundary rather than an oversight — but the boundary is now the only reason it is open.
Decide
1. Do the checkout session routes take the cart ID from the shopper envelope instead of the request? This is the same move #491 made for carts — the server supplies the ID that constitutes access control, and the browser names an operation rather than a resource. If yes, EPCheckoutProvider stops fetching the ID and getCartIdFromSession loses its last caller.
2. What happens to the registered cartId prop (epCheckoutProviderMeta, EPCheckoutProvider.tsx:629, described as "Explicit cart ID; falls back to cookie")? It cannot be removed under ADR-0004. Either it stays a live override — which means the checkout routes still accept a browser-supplied ID, and question 1 buys nothing — or it becomes an inert registration. Note that a designer-facing override is what #518 accepted the death of on EP Shopper Context, for this same reason.
3. Can epCartId then come off SESSION_ALLOWLIST? Only once nothing in the browser reads it. Check for consumers outside this package before removing — it has been browser-readable since PRD #273.
Not a blocker
Independent of the ADR-0003 migration and it must not gate that release. It is filed on the milestone because that is where the finding came from, not because the migration waits on it.
The exposure is pre-existing and unchanged by the migration: it is neither introduced nor worsened by any issue in this set.
ADR-0003: plasmicpkgs/commerce-providers/elastic-path/docs/adr/0003-one-session-for-elastic-path-identity.md.
ADR-0004 (why registered props cannot be removed): .../0004-registered-surfaces-are-append-only.md.
Map: #486. Surfaced while writing the before/after of the session model.
In brief
A shopper's basket is addressed by its ID, and on Elastic Path that ID is the entire access boundary: anyone holding it can read and write that basket, because the credential needed to act on one can be minted by anyone from the store's public ID. So the ID is a key, not a label.
ADR-0003 concluded that keeping the ID in server custody is the whole defence. But the storefront still hands it to the browser — the session endpoint returns it, and a script on the page can read it and replay it later from anywhere, for as long as the basket lives.
This issue decides whether to close that, and it is a checkout decision rather than an identity one, which is why ADR-0003 did not reach it.
Why it is still open
epCartIdis onSESSION_ALLOWLIST(auth/ep-plugin/auth-routes.ts:28), so/api/ep/get-sessionreturns it to any script on the page. Two components fetch it viagetCartIdFromSession()(cart/cart-session.ts:30):EPPromoCodeInput(:305,:345) — dies with Rebuild EPPromoCodeInput on a named cart-adjustment operation #541. Stops needing it.EPCheckoutProvider(:238) — survives. It resolves the ID on mount and passes it touseCheckout({ cartId }), which drives all four checkout steps against/api/checkout/sessions/*. The browser holds the ID for the whole checkout.ADR-0003 scoped checkout out deliberately, and #491 said so explicitly:
/api/checkout/sessions/*"stays separate: it holds the merchantclient_credentialssecret and computes authoritative money, and merges into neither browser-facing route." The surviving exposure sits on the checkout side of that boundary, so it is a boundary rather than an oversight — but the boundary is now the only reason it is open.Decide
1. Do the checkout session routes take the cart ID from the shopper envelope instead of the request? This is the same move #491 made for carts — the server supplies the ID that constitutes access control, and the browser names an operation rather than a resource. If yes,
EPCheckoutProviderstops fetching the ID andgetCartIdFromSessionloses its last caller.2. What happens to the registered
cartIdprop (epCheckoutProviderMeta,EPCheckoutProvider.tsx:629, described as "Explicit cart ID; falls back to cookie")? It cannot be removed under ADR-0004. Either it stays a live override — which means the checkout routes still accept a browser-supplied ID, and question 1 buys nothing — or it becomes an inert registration. Note that a designer-facing override is what #518 accepted the death of on EP Shopper Context, for this same reason.3. Can
epCartIdthen come offSESSION_ALLOWLIST? Only once nothing in the browser reads it. Check for consumers outside this package before removing — it has been browser-readable since PRD #273.Not a blocker
Independent of the ADR-0003 migration and it must not gate that release. It is filed on the milestone because that is where the finding came from, not because the migration waits on it.
The exposure is pre-existing and unchanged by the migration: it is neither introduced nor worsened by any issue in this set.
ADR-0003:
plasmicpkgs/commerce-providers/elastic-path/docs/adr/0003-one-session-for-elastic-path-identity.md.ADR-0004 (why registered props cannot be removed):
.../0004-registered-surfaces-are-append-only.md.Map: #486. Surfaced while writing the before/after of the session model.