Skip to content

[Draft] GDPR cookie consent for non-FedRAMP portals (GA + orestbida/cookieconsent + CloudFront audit trail) #3037

@NoopDog

Description

@NoopDog

Status: Draft. Decisions and acceptance criteria below are a starting point for discussion, not a finalized plan. Feedback welcome before scoping.

Summary

Add a GDPR-compliant cookie consent flow to the non-FedRAMP portals that currently load Google Analytics without an opt-in gate. Use a self-hosted open-source banner (orestbida/cookieconsent), hard-block GA until the user accepts, and use CloudFront access logs as the server-side consent audit trail so we avoid standing up a backend or adopting a third-party CMP.

FedRAMP portals are out of scope for this issue and will be handled separately — GA itself is not FedRAMP authorized, and the right answer there is a cookieless self-hosted analytics tool, not a consent banner.

Context

  • Portals currently inject gtag.js on page load, setting _ga / _ga_* cookies before consent. Not GDPR compliant.
  • Commercial CMPs add recurring cost, a third-party script on every page, and another data processor to disclose.
  • We do not use AdSense / Ad Manager / AdMob, so Google's IAB-TCF Certified-CMP mandate (orestbida/cookieconsent discussion #523) does not apply.
  • GA4 Google Signals should be verified off on each property before shipping — if on, GA pulls in ads consent signals we otherwise don't need.

Decisions

  1. Library: orestbida/cookieconsent (MIT, vanilla, ~30KB). Styled via CSS custom properties to match findable-ui tokens.
  2. Categories: necessary + analytics only.
  3. GA pattern: hard-block. gtag.js injected only after analytics grant; _ga* cleared and page reloaded on revoke. Consent Mode v2 signals set for forward compatibility.
  4. Client receipt: library's default localStorage record {consentId, categories, revision, timestamp}. Footer "Cookie preferences" link reopens the panel.
  5. Server audit trail: on accept/reject/change, fire one GET /consent/{consentId}/{revision}/{categoriesHash} → CloudFront behavior returns 204. Access logs to S3, queryable via Athena.
  6. Revision versioning: bump revision to invalidate stale receipts and re-prompt.
  7. Geo-targeting: show banner to all users.
  8. Retention: S3 lifecycle on consent-log prefix, duration TBD with legal.

Architecture

Browser                              CloudFront + S3 (existing SPA host)
┌────────────────────────┐          ┌────────────────────────────┐
│ SPA loads              │◀─────────│ index.html, JS, CSS        │
│  ↓ cookieconsent init  │          └────────────────────────────┘
│  ↓ read localStorage   │
│  ↓ valid + current?    │          ┌────────────────────────────┐
│   yes → inject GA      │  accept  │ /consent/* behavior        │
│   no  → banner         │─────────▶│ returns 204                │
│  ↓ accept              │  GET     └─────────────┬──────────────┘
│  write receipt         │                        │ access logs
│  beacon /consent/...   │                        ▼
│  inject gtag.js        │          ┌────────────────────────────┐
└────────────────────────┘          │ S3 logs + lifecycle        │
                                     │ + Athena table             │
                                     └────────────────────────────┘

Scope

  • One shared consent package in the monorepo, consumed by each non-FedRAMP portal.
  • Theming hooks mapping --cc-* onto findable-ui tokens.
  • CloudFront behavior + S3 log bucket + lifecycle via existing IaC pattern.
  • Athena table DDL committed with the infra.
  • Privacy policy updates on each affected portal.

Out of scope: FedRAMP portals, migration to cookieless analytics, IAB TCF integration.

Acceptance criteria

  • GA not loaded on first visit; no _ga* cookies before consent.
  • Accepting analytics loads gtag.js and GA flows normally.
  • Rejecting/revoking clears _ga* and prevents further GA activity this session.
  • Footer "Cookie preferences" link reopens the panel on every page.
  • Every accept/reject/change fires one beacon to /consent/{consentId}/{revision}/{categoriesHash}.
  • Consent-path access logs queryable via Athena by consentId.
  • Bumping revision re-prompts previously consented users.
  • Banner matches each portal's findable-ui theme (light + dark).
  • Privacy policy updated on each affected portal.

Spike / open questions

  • Confirm GA4 Google Signals is off on each property.
  • Confirm log retention duration with legal.
  • Same CloudFront distribution as the SPA or dedicated one for /consent/*?
  • Shared-package location: findable-ui or new package?

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions