CCPA § 1798.120(a) grants every consumer — not just authenticated users — the right to opt out of the sale or sharing of their personal information. § 1798.135(a)(2) explicitly prohibits businesses from requiring consumers to create an account or log in to exercise this right. An opt-out page that returns a 404, requires authentication, stores the preference in sessionStorage only, or resets on next visit fails on multiple statutory requirements simultaneously. These failures also cannot be fixed client-side at runtime — they require a working deployment, an accessible route, and persistent server-side storage as the fallback when cookies are cleared.
Low because functional failures of an existing opt-out mechanism are harder to detect than its total absence, but requiring authentication to opt out is an explicit statutory prohibition under CCPA § 1798.135(a)(2).
Verify the /do-not-sell route is deployed and returns a 200, works without authentication, stores preference in a one-year cookie (not sessionStorage), and displays an immediate confirmation.
// app/do-not-sell/page.tsx — no auth required, persistent storage, clear confirmation
'use client'
import { useState } from 'react'
export default function DoNotSellPage() {
const [done, setDone] = useState(false)
async function optOut() {
document.cookie = 'ccpa_opt_out=1; max-age=31536000; path=/; SameSite=Lax; Secure'
try { await fetch('/api/privacy/opt-out', { method: 'POST' }) } catch {}
setDone(true)
}
return (
<main>
<h1>Do Not Sell or Share My Personal Information</h1>
<p>No account required to exercise this right (CCPA § 1798.120).</p>
{done ? (
<div role="alert"><strong>Opt-out recorded.</strong> Preference saved for one year.</div>
) : (
<button onClick={optOut}>Opt Out of Sale/Sharing</button>
)}
</main>
)
}
Test the route with a fresh incognito session — no cookies, no login — and confirm the button works and the confirmation renders.
ID: ccpa-readiness.opt-out.opt-out-mechanism-functional
Severity: low
What to look for: Test the usability of the opt-out mechanism. Check whether the "Do Not Sell or Share" link navigates to a working page (not a 404 or a page that only describes the right). Verify the opt-out form or button works without the user needing to log in or create an account — CCPA explicitly prohibits requiring authentication to exercise opt-out rights. Check whether the mechanism works with JavaScript disabled or on low-end browsers (it does not need to, but check for graceful degradation). Verify the opt-out confirmation is immediate and clear — the user should know their opt-out was recorded. Check if the opt-out is preserved across sessions (persistent) and across subdomains if applicable. Count all instances found and enumerate each.
Pass criteria: The opt-out mechanism is reachable via the footer link without authentication. The mechanism functions correctly (form submits, preference is stored, confirmation is displayed). Opt-out preference persists across browser sessions. At least 1 implementation must be confirmed.
Fail criteria: The opt-out page returns a 404 or is empty. Opt-out mechanism requires authentication. Form submits but no confirmation is displayed. Opt-out preference is stored only in sessionStorage (clears on tab close) rather than a persistent cookie or server-side record. Do NOT pass if the opt-out mechanism requires authentication — CCPA requires opt-out to work without an account.
Skip (N/A) when: Application does not sell or share PI — document.
Detail on fail: Example: "'Do Not Sell or Share' link in footer leads to /do-not-sell which returns a 404 error." or "Opt-out form requires signing in before submission — this is an impermissible barrier." or "Opt-out stored in sessionStorage only; preference lost when browser tab is closed.".
Remediation: Verify the opt-out route is deployed and accessible, ensure persistent storage, and add clear confirmation UI:
// app/do-not-sell/page.tsx — verify this route exists and is functional
export default function DoNotSellPage() {
const [status, setStatus] = useState<'idle' | 'done'>('idle')
async function optOut() {
// Persistent cookie — survives session
document.cookie = 'ccpa_opt_out=1; max-age=31536000; path=/; SameSite=Lax; Secure'
// Also persist server-side for authenticated users
try {
await fetch('/api/privacy/opt-out', { method: 'POST' })
} catch {
// Best-effort server persistence; cookie is the fallback
}
setStatus('done')
}
return (
<main>
<h1>Do Not Sell or Share My Personal Information</h1>
<p>
Under California law (CCPA/CPRA), you have the right to direct us not
to sell or share your personal information with third parties.
No account is required to exercise this right.
</p>
{status === 'done' ? (
<div role="alert">
<strong>Your opt-out has been recorded.</strong> We will not sell or
share your personal information. This preference is saved for one year.
</div>
) : (
<button onClick={optOut}>
Opt Out of Sale/Sharing
</button>
)}
</main>
)
}