Repository navigation
Sync cart buyer identity when auth status changes - #3913
Conversation
| onAuthenticated: attachCartBuyerIdentity, | ||
| onTokenRefresh: refreshCartBuyerIdentity, | ||
| onLogout: clearCartBuyerIdentity, |
There was a problem hiding this comment.
note that we dont do it automatically as the use case is not for every customer.
in addition, we dont want to control how users store the cart id cookie and binding a non httpOnly cookie to a httpOnly session was flagged by security review from one of my subagents
this way we leave the responsibility to the implementer
this may be bad, because there is a lot more wiring up to do, im willing to discuss whether this is the most sane approach or an "auto sync" is a better idea
graygilmore
left a comment
There was a problem hiding this comment.
Not entirely sure how to 🎩 this one so I'm leaving the one critical thing that the bots found.
frandiox
left a comment
There was a problem hiding this comment.
Thanks for working on this! I like the direction but at the same time I feel we should make things a bit simpler for the most common path? 🤔
What if we provide something like this? (name tbd)
const cartBuyerIdentity = createCartBuyerIdentitySync({
customerSession,
});
const cartHandlers = createCartServerHandlers({
cartBuyerIdentity,
// Or if we want to have top level hooks:
// onCartCreated: cartBuyerIdentity.onCartCreated,
});
const customerAccountHandlers = createCustomerAccountServerHandlers({
cartBuyerIdentity
// Or with hooks:
// ...cartBuyerIdentity.customerAccountHooks,
});Hydrogen already writes the cart cookie and receives the trusted cart ID directly from the Storefront API when it creates the cart. The helper would save that same ID in protected session storage and commit it on the response. It would not trust a cart ID sent by the browser.
createCartBuyerIdentitySync would run cartBuyerIdentityUpdate after login and refresh, clear the identity on logout, and own cookie cleanup and request ordering. The helper is opt-in and tree-shakeable.
We could still consider keeping the the lifecycle hooks if we want to be extra flexible as a low-level API and this helper can use it. If not, the helper can keep that lifecycle wiring internal and expose only the cart-specific API.
my original idea was on the lines of a security subagent flagged that this could mean we attach a non httpOnly cookie that can be fiddled with by the user, to an encrypted user session. to work around that i exposed the hooks that make it possible to do it yourself, but it's now down to the implementer to keep the cart cookie safe however they want i would now suggest we do it like the initial idea (binding customer account with cart handlers like my code example) and consider either encrypting the cart cookie or just live with it, it's not really a security concern, the cart id cookie is already exposed and does nothing, attaching a modified cart id to your session means nothing at all to us it felt much simpler to operate in the initial implementatioin than it does not (surely a lot less docs) |
Associate new carts with currently authenticated Customer Account sessions and expose lifecycle hooks for application-owned synchronization after authentication, refresh, and logout. Keep existing-cart ownership verification outside Hydrogen, document protected server-side cart bindings and fail-safe logout cleanup, and preserve session state with sanitized lifecycle-hook failures. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde
The change adds public Customer Account lifecycle hooks and cart handler options, so consumers receive it as an additive minor release. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde
Expose the exact authorization token and a discriminated refresh result so integrations can synchronize cart buyer identity without rereading session storage or conflating transient and definitive refresh failures. Restrict token-bearing hooks to factory-created sessions while preserving standard routes and logout hooks for custom CustomerSession implementations. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde
Replaces the public onAuthenticated/onTokenRefresh/onLogout lifecycle hooks with a cartServerHandlers option: Hydrogen now owns the sync loop (attach on authorize/refresh, detach on definitive rejection and logout). Sync is best-effort and never blocks the route redirect; a failed detach during logout expires the cart cookie as fail-safe. Assisted-By: devx/5bed7c10-b34f-44d6-afc4-4a93ef26e5ca
Assisted-By: devx/48ff1d59-2f7a-4e3c-a981-4d1802189c64
…lers Assisted-By: devx/48ff1d59-2f7a-4e3c-a981-4d1802189c64
19bfb33 to
eaaaf9b
Compare
frandiox
left a comment
There was a problem hiding this comment.
This version is much simpler to use 🙌
Assisted-By: devx/b81bba60-d341-4412-9485-da84600941e0
…dentity-sync # Conflicts: # packages/hydrogen/src/core/cart/queries.ts # packages/hydrogen/src/customer-account/session.ts
* feat: sync cart customer identity Associate new carts with currently authenticated Customer Account sessions and expose lifecycle hooks for application-owned synchronization after authentication, refresh, and logout. Keep existing-cart ownership verification outside Hydrogen, document protected server-side cart bindings and fail-safe logout cleanup, and preserve session state with sanitized lifecycle-hook failures. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde * chore: mark cart identity release as minor The change adds public Customer Account lifecycle hooks and cart handler options, so consumers receive it as an additive minor release. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde * feat: pass tokens to customer lifecycle hooks Expose the exact authorization token and a discriminated refresh result so integrations can synchronize cart buyer identity without rereading session storage or conflating transient and definitive refresh failures. Restrict token-bearing hooks to factory-created sessions while preserving standard routes and logout hooks for custom CustomerSession implementations. Assisted-By: devx/4da10d8d-5225-495f-beb2-40d5a959dcde * feat: wire cart buyer identity sync into customer account handlers Replaces the public onAuthenticated/onTokenRefresh/onLogout lifecycle hooks with a cartServerHandlers option: Hydrogen now owns the sync loop (attach on authorize/refresh, detach on definitive rejection and logout). Sync is best-effort and never blocks the route redirect; a failed detach during logout expires the cart cookie as fail-safe. Assisted-By: devx/5bed7c10-b34f-44d6-afc4-4a93ef26e5ca * fixup! feat: sync cart customer identity Assisted-By: devx/48ff1d59-2f7a-4e3c-a981-4d1802189c64 * fixup! feat: wire cart buyer identity sync into customer account handlers Assisted-By: devx/48ff1d59-2f7a-4e3c-a981-4d1802189c64 * fix: harden cart customer identity sync Assisted-By: devx/b81bba60-d341-4412-9485-da84600941e0
TL;DR: Cart handlers can now associate newly created carts with the current Customer Account session, while Customer Account routes expose lifecycle hooks for application-owned synchronization of existing carts.
Before
Cart creation did not include Customer Account buyer identity, and the standard authorization, refresh, and logout routes offered no integration points for related session work.
After
getVerifiedCartIdmust resolve a cart ID from protected server-side ownership state, not an unsigned client-controlled cookie or request value.What this changes
logged_in=true.authenticated,transient, orunauthenticatedresult.Developer impact
Includes a minor changeset for
@shopify/hydrogen. The new cart option, lifecycle hooks, handler types, and documentation are additive; existing handler configuration remains unchanged. Token-bearing hooks require the session returned bycreateCustomerSession; custom session implementations can continue using standard routes andonLogout.Out of scope
Risk