When authenticated users' carts live only in localStorage, every device switch silently starts a fresh cart. A shopper who builds a cart on desktop, then completes checkout on mobile, finds an empty cart and abandons. ISO 25010:2011 functional correctness and CWE-20 both apply: cart state is user data that must persist correctly across sessions. Cross-device cart loss is a documented e-commerce abandonment driver — Baymard Institute consistently ranks it among top reasons for checkout failure. Without backend sync, any tab closure, browser clear, or device change wipes the cart irrecoverably.
High because cart data loss for authenticated users is a direct revenue impact that occurs silently on every multi-device session without any error signal.
Extend the cart hook at src/hooks/useCart.ts to read from and write to a backend cart API whenever a session is active. On useEffect([user?.id]), fetch the server cart; on every mutation, await fetch('/api/user/:id/cart', { method: 'POST', body: JSON.stringify({ productId }) }) and update state from the response:
// src/hooks/useCart.ts
const addToCart = async (productId: string) => {
if (user) {
const res = await fetch(`/api/user/${user.id}/cart`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ productId }),
})
const data = await res.json()
setCart(data.items)
} else {
// fall back to localStorage for guests
setCart(prev => [...prev, { productId, quantity: 1 }])
}
}
Keep localStorage as the guest fallback and merge on login.
ID: ecommerce-cart-ux.cart-management.cross-device-sync
Severity: high
What to look for: List all auth-related providers or hooks in the project (e.g., useAuth, useSession, AuthProvider). For each cart mutation (add, remove, update quantity), check whether the operation makes an API call to persist changes on the server when a user is authenticated. Count the cart mutation functions and classify each as: (a) local-only, (b) API-synced, (c) hybrid. Quote the API endpoint used for cart sync (e.g., fetch('/api/cart', { method: 'POST' }) in src/hooks/useCart.ts).
Pass criteria: At least 1 API call fetches cart data from the backend on page load when user is authenticated. At least 1 cart mutation (add/remove) syncs changes to the backend via API. Report: "X cart mutations found, Y sync to backend via API endpoint Z."
Fail criteria: Cart is stored in localStorage or cookies only, even for authenticated users. No API calls found for cart operations when auth context is present.
Do NOT pass when: An API endpoint exists but is never called from the frontend, or when the fetch is wrapped in a TODO/commented-out block.
Skip (N/A) when: The project has no user authentication (no login, signup, or session management). Unauthenticated-only sites skip this check.
Detail on fail: Example: "Found useAuth provider in src/providers/AuthProvider.tsx. Cart uses localStorage only — 0 of 4 cart mutations make API calls. Logged-in users cannot sync cart across devices."
Cross-reference: For local persistence as a fallback, see the cart-persistence check above. For API route security on cart endpoints, the API Security audit covers authentication middleware and authorization checks.
Remediation: Add backend sync in your cart hook when authenticated, typically at src/hooks/useCart.ts:
// src/hooks/useCart.ts
async function useAuthenticatedCart() {
const { user } = useAuth()
const [cart, setCart] = useState([])
useEffect(() => {
if (user) {
fetch(`/api/user/${user.id}/cart`)
.then(res => res.json())
.then(data => setCart(data.items))
}
}, [user?.id])
const addToCart = async (productId) => {
const response = await fetch(`/api/user/${user.id}/cart`, {
method: 'POST',
body: JSON.stringify({ productId })
})
const data = await response.json()
setCart(data.items)
}
return { cart, addToCart }
}