# Third-party sharing stops when consumer opts out

- **Pattern:** `ab-000631` (`ccpa-readiness.opt-out.third-party-sharing-gated`)
- **Severity:** medium
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-000631
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-000631)

## Why it matters

Recording an opt-out preference without actually stopping data sharing is a CCPA § 1798.120 violation more egregious than having no opt-out at all — it deceives the consumer into believing their request was honored. The most common pattern: opt-out cookie is set correctly, suppressing one pixel (e.g., Facebook), while server-side Segment `track()` calls or a Google Analytics `identify()` still fire unconditionally because they were wired into API route handlers that never check the opt-out flag. Each page load where a third-party sharing call fires for an opted-out consumer is a separate violation. This check requires tracing the enforcement gap, not just the preference storage.

## Severity rationale

Medium because the gap between stored opt-out preference and actual enforcement of that preference across all sharing touchpoints is a concrete CCPA § 1798.120 violation per sharing event, even when the opt-out UI exists.

## Remediation

Create a centralized `isUserOptedOut()` helper that both client-side pixels and server-side analytics calls must pass through. Enumerate every third-party sharing touchpoint and confirm each one is gated.

```ts
// lib/analytics.ts — single source of truth for opt-out check
import { cookies } from 'next/headers'

export async function isUserOptedOut(userId?: string): Promise<boolean> {
  if (userId) {
    const pref = await db.privacyPreference.findFirst({ where: { userId, type: 'sale_sharing' } })
    if (pref) return true
  }
  const cookieStore = cookies()
  return cookieStore.get('ccpa_opt_out')?.value === '1'
}

// Server-side event — every analytics call uses this wrapper
export async function trackServerEvent(event: string, props: Record<string, unknown>, userId?: string) {
  if (await isUserOptedOut(userId)) return
  await segment.track({ event, properties: props, userId })
}
```

Search `src/` for `segment.track`, `gtag`, `fbq`, `analytics.identify`, and `mixpanel.track` calls and confirm each one is wrapped or gated. Document the gating coverage in `PRIVACY_OPERATIONS.md`.

## Detection

- **ID:** `third-party-sharing-gated`
- **Severity:** `medium`
- **What to look for:** This check specifically verifies that the opt-out preference is enforced at the code level — that third-party data sharing actually stops, not just that an opt-out preference is recorded. Trace the execution path: when a page loads with an active opt-out preference (cookie or server-side flag), do advertising pixels (Facebook, Google Ads, TikTok) still fire? Check client-side analytics initialization code: does it read the opt-out cookie before loading pixel scripts? Check server-side data flows: are there any server-side API calls that send user data to third parties (data brokers, advertising platforms, enrichment services) that are not gated on the opt-out flag? Check webhook handlers: if user events are piped to Segment, Amplitude, or similar, are those calls suppressed when the user is opted out? Enumerate all third-party sharing touchpoints: list all pixel scripts, server-side event calls, and webhook handlers that transmit PI. For each, classify as gated or ungated on opt-out preference.
- **Pass criteria:** All third-party sharing pathways — client-side pixels, server-side API calls to advertising platforms, and analytics identify/track calls — are gated on the consumer's opt-out status. When an opt-out is active, no personal information is shared with third parties for advertising or sharing purposes. Report even on pass: "X of Y third-party sharing pathways confirmed gated on opt-out preference. All advertising pixels suppressed when opt-out active." At least 1 implementation must be confirmed.
- **Fail criteria:** Opt-out cookie is set but one or more third-party sharing pathways continue to fire. Server-side data flows to advertising or analytics platforms are not gated on opt-out status. Analytics `identify()` calls fire unconditionally regardless of opt-out state.
- **Skip (N/A) when:** Application has no third-party data sharing — no advertising pixels, no behavioral analytics that share PI, no data broker integrations.
- **Detail on fail:** Example: `"Opt-out cookie correctly suppresses Facebook Pixel but Google Analytics identify() call fires unconditionally in src/app/providers.tsx regardless of opt-out state."` or `"Server-side Segment track() calls in API route handlers are not gated on user's opt-out preference."`.
- **Remediation:** Audit every data-sharing touchpoint and add opt-out gating:

  ```ts
  // lib/analytics.ts — centralized opt-out check for all sharing
  import { cookies } from 'next/headers'

  // Server-side: check opt-out from cookie or database
  export async function isUserOptedOut(userId?: string): Promise<boolean> {
    if (userId) {
      const pref = await db.privacyPreference.findFirst({
        where: { userId, type: 'sale_sharing' }
      })
      if (pref) return true
    }
    const cookieStore = cookies()
    return cookieStore.get('ccpa_opt_out')?.value === '1'
  }

  // Server-side event tracking — gated
  export async function trackServerEvent(
    event: string,
    properties: Record<string, unknown>,
    userId?: string
  ) {
    if (await isUserOptedOut(userId)) return
    await segment.track({ event, properties, userId })
  }

  // Client-side — gate all pixel firing
  export function trackClientEvent(event: string, properties: Record<string, unknown>) {
    const optedOut = document.cookie.includes('ccpa_opt_out=1') ||
      navigator.globalPrivacyControl === true
    if (optedOut) return

    fbq?.('track', event, properties)
    gtag?.('event', event, properties)
  }
  ```

---

## External references

- ccpa §1798.120
- ccpa §1798.140(ah)
- cwe CWE-359
- owasp A01

Taxons: privacy-consent, regulatory-conformance

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