# Cart syncs across devices when authenticated

- **Pattern:** `ab-001097` (`ecommerce-cart-ux.cart-management.cross-device-sync`)
- **Severity:** high
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001097
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001097)

## Why it matters

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.

## Severity rationale

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.

## Remediation

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:

```tsx
// 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.

## Detection

- **ID:** `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`:

  ```tsx
  // 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 }
  }
  ```

---

## External references

- cwe CWE-20
- iso-25010 functional-suitability.functional-correctness

Taxons: user-experience, data-integrity

HTML version: https://auditbuffet.com/patterns/ab-001097
