Users can withdraw consent from settings; withdrawal stops processing
Why it matters
GDPR Art. 7(3) is explicit: withdrawal of consent must be as easy as giving it, and processing must stop immediately upon withdrawal. A settings toggle that logs a withdrawal but continues sending events to analytics or ad networks is a phantom control — it creates the appearance of compliance while the violation continues. CCPA §1798.120 similarly requires that opt-out of data sale take effect within 15 business days. A backend that ignores the consent flag converts every post-withdrawal data collection event into a separate unlawful processing incident.
Severity rationale
High because continued processing after documented withdrawal transforms a configuration issue into active, ongoing GDPR Art. 7(3) violations with each additional data collection event.
Remediation
Check the most recent consent record in every data collection code path — do not cache the consent state in memory across requests. Add a middleware guard in src/middleware.ts or in the relevant route handler:
async function shouldCollectAnalytics(userId: string): Promise<boolean> {
const latest = await db.userConsentAudit.findFirst({
where: { userId, processingType: 'analytics' },
orderBy: { consentedAt: 'desc' }
});
return latest?.consentGiven ?? false;
}
// In your analytics event handler
app.post('/api/events', async (req, res) => {
if (!(await shouldCollectAnalytics(req.user.id))) {
return res.status(204).end(); // silently discard
}
await trackEvent(req.body);
res.status(200).end();
});
Flush any client-side analytics queue immediately on withdrawal — don't wait for the next page load.
Detection
-
ID:
consent-withdrawal -
Severity:
high -
What to look for: Enumerate every relevant item. Examine settings UI for consent withdrawal mechanisms. Check whether users can disable each non-essential processing type after opting in. Verify that withdrawal is immediately reflected — data collection stops right away. Check for withdrawal workflows and backend enforcement.
-
Pass criteria: At least 1 of the following conditions is met. Settings page displays all consented processing types with toggles to withdraw. Withdrawal is immediate and causes data collection to stop. Logs show withdrawal with timestamp.
-
Fail criteria: No withdrawal mechanism in settings. Users cannot disable analytics after enabling. Data collection continues even after withdrawal toggle.
-
Skip (N/A) when: Never — withdrawal is a user right.
-
Detail on fail: Describe the limitation. Example:
"Settings page shows analytics toggle but changes have no effect. Backend continues tracking regardless of toggle state."or"No UI to manage consent preferences after signup." -
Remediation: Implement withdrawal with immediate enforcement:
// Settings component export function ConsentSettings({ userId }) { const [analyticsEnabled, setAnalyticsEnabled] = useState(false); const handleToggle = async (type: string, enabled: boolean) => { await fetch('/api/consent', { method: 'POST', body: JSON.stringify({ processingType: type, consentGiven: enabled }) }); // Immediately stop collection if (!enabled && type === 'analytics') { window.analyticsQueue?.flush(); window.analyticsQueue = null; } }; return ( <label> <input type="checkbox" checked={analyticsEnabled} onChange={(e) => { setAnalyticsEnabled(e.target.checked); handleToggle('analytics', e.target.checked); }} /> Allow anonymous usage analytics </label> ); }Enforce withdrawal in backend:
async function shouldCollectAnalytics(userId: string) { const consent = await db.userConsentAudit.findFirst({ where: { userId, processingType: 'analytics' }, orderBy: { consentedAt: 'desc' } }); return consent?.consentGiven ?? false; } // Middleware app.post('/api/events', async (req, res) => { const userId = req.user.id; const shouldTrack = await shouldCollectAnalytics(userId); if (!shouldTrack) { return res.status(200).json({ accepted: false }); } // Process event });
External references
- gdpr · Art. 7(3) — Right to withdraw consent at any time
- ccpa · §1798.120 — Right to opt-out of sale or sharing of personal information
- eprivacy · Art. 5(3) — Right to withdraw consent for non-essential processing
Taxons
History
- 2026-04-18·v1.0.0·Initial import from community-privacy-controls·automated