Skip to content

Conversation

@gilluminate
Copy link
Contributor

@gilluminate gilluminate commented Jan 14, 2026

Ticket ENG-2135

Description Of Changes

Read all automated consent sources (GPC, migrated consent from third-party providers, and notice consent strings) synchronously during initialization before the FidesInitialized event fires. This ensures that when Fides broadcasts that consent is ready to be read, all automated consent has been fully evaluated and applied.

Key changes:

  • Introduced getAutomatedConsentContext() to read all automated consent sources synchronously at the start of initialization
  • Refactored calculateAutomatedConsent() as a pure function that computes consent from the automated context
  • Applied automated consent to experience and cookie before FidesInitialized fires
  • Made API persistence call asynchronous (non-blocking) since consent is already calculated and stored locally

Code Changes

  • Refactored consent context reading to be synchronous in consent-context.ts
  • Created getAutomatedConsentContext() to gather GPC, migrated consent, and notice consent strings early
  • Split automated consent logic into calculation (calculateAutomatedConsent) and API persistence (saveAutomatedPreferencesToApi) in automated-consent.ts
  • Updated initialize.ts to calculate and apply automated consent before updating cookie/experience and firing events
  • Created shared buildConsentPreferencesArray() utility in shared-consent-utils.ts to reduce duplication
  • Updated all three init files (fides.ts, fides-headless.ts, fides-tcf.ts) to use the new automated consent flow
  • Added comprehensive Cypress tests verifying consent is immediately available when FidesInitialized fires

Steps to Confirm

Create an experience with the following Notices:

  • Analytics
  • Data Sales and Sharing
  • Essential
  • Marketing

By default, when loading the demo page, the Consent JSON should look like:

{
  "analytics": false,
  "data_sales_and_sharing": true,
  "essential": true,
  "marketing": false
}

Test GPC Automated Consent (Chrome/Non-GPC browsers)

  1. Enable GPC in browser (or use ?globalPrivacyControl=true query parameter)
  2. Load a page with Fides configured with GPC-enabled privacy notices
  3. Verify consent is immediately available:
    • Check that window.Fides.consent contains the GPC opt-out values (false) as soon as FidesInitialized fires
{
  "analytics": true, // not a GPC notice
  "data_sales_and_sharing": false, // GPC applied
  "essential": true, // notice only
  "marketing": false // GPC applie
}
  • Confirm no delay occurs
  • Confirm Fides cookie is created with correct values
  1. Verify API call was made:
    • Check network tab for a single PATCH to /privacy-preference with method: "gpc"
    • Confirm the preferences include preference: "opt_out" for GPC-flagged notices
  2. Verify consent updates through user interactions:
    • Click an "Accept all" button on the banner
    • Confirm GPC consent is overridden
    • Verify a second API call is made with method: "accept" for the manual interaction

Test Notice Consent String (fidesString)

  1. Note: it's ok to leave GPC turned on for the remaining testing, since both Notice Consent and Migrated Consent should take priority over GPC.
  2. Set a fides_string cookie with notice consent encoded (e.g., ",,,eyJhbmFseXRpY3MiOjEsImRhdGFfc2FsZXNfYW5kX3NoYXJpbmciOjEsIm1hcmtldGluZyI6MX0=")
// Set this cookie in browser DevTools Console:
document.cookie = "fides_string=,,,eyJhbmFseXRpY3MiOjEsImRhdGFfc2FsZXNfYW5kX3NoYXJpbmciOjEsIm1hcmtldGluZyI6MX0=; path=/";
  1. Load Fides
  2. Verify consent is immediately available:
    • Check that window.Fides.consent and the browser cookie contain the decoded consent values
    • Verify no delay before consent appears
    • Confirm Fides cookie is created with correct "opt in to all" values
{
  "analytics": true,
  "data_sales_and_sharing": true,
  "essential": true,
  "marketing": true
}
  1. Verify API call:
    • Check for a PATCH to /privacy-preference with method: "script"
    • Confirm preferences match the decoded fides string values

Test Migrated Consent (OneTrust → Fides)

  1. Note: you don't need to remove the "fides_string" cookie above, because migrated consent should take priority over that. Leaving it in place helps test that priority level for the below tests.
  2. Set an OneTrust cookie with existing consent preferences. Use one of these examples
// Example Values:
// All Accepted:
document.cookie = "OptanonConsent=isGpcEnabled=0&datestamp=Fri+Mar+07+2025+19%3A51%3A06+GMT%2B0100&version=202409.1.0&isIABGlobal=false&hosts=&consentId=f9c105b8-48e3-401a-87e9-363ad1f77531&interactionCount=1&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A1%2CC0004%3A1; path=/";
// Mixed Preferences (C0001=yes, C0002=yes, C0004=no):
document.cookie = "OptanonConsent=isGpcEnabled=0&datestamp=Fri+Mar+07+2025+19%3A51%3A06+GMT%2B0100&version=202409.1.0&isIABGlobal=false&hosts=&consentId=f9c105b8-48e3-401a-87e9-363ad1f77531&interactionCount=1&landingPath=NotLandingPage&groups=C0001%3A1%2CC0002%3A1%2CC0004%3A0; path=/";
// All Rejected:
document.cookie = "OptanonConsent=isGpcEnabled=0&datestamp=Fri+Mar+07+2025+19%3A51%3A06+GMT%2B0100&version=202409.1.0&isIABGlobal=false&hosts=&consentId=f9c105b8-48e3-401a-87e9-363ad1f77531&interactionCount=1&landingPath=NotLandingPage&groups=C0001%3A0%2CC0002%3A0%2CC0004%3A0; path=/";
  1. Configure Fides with otFidesMapping to map OneTrust groups to Fides notices
// Add the OT mapping to your Fides initialization:
window.fides_overrides = {
  ot_fides_mapping: encodeURIComponent(
    JSON.stringify({
      C0001: ["essential"],
      C0002: ["analytics_opt_out"],
      C0004: ["analytics", "marketing"],
    }),
  ),
};
  1. Ensure NO existing Fides cookie exists
  2. Load Fides
  3. Verify migrated consent is immediately available:
    • Check that window.Fides.consent contains the migrated OneTrust values
    • Confirm consent appears synchronously
    • Confirm Fides cookie is created with correct values
    • Confirm notice-only notices are never set to false (eg. essential)
  4. Verify API call:
    • Check for a PATCH with the migrated consent method (e.g., "onetrust")

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • Documentation issue created in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

@vercel
Copy link
Contributor

vercel bot commented Jan 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Review Updated (UTC)
fides-plus-nightly Ignored Ignored Preview Jan 14, 2026 9:24pm
fides-privacy-center Ignored Ignored Jan 14, 2026 9:24pm

@gilluminate gilluminate force-pushed the gill/ENG-2135/fides-initializes-before-gpc-is-read branch 8 times, most recently from 01d35fb to 11cb907 Compare January 14, 2026 20:10
@gilluminate gilluminate force-pushed the gill/ENG-2135/fides-initializes-before-gpc-is-read branch from 11cb907 to 06440ce Compare January 14, 2026 20:10
@gilluminate gilluminate marked this pull request as ready for review January 14, 2026 20:11
@gilluminate gilluminate requested a review from a team as a code owner January 14, 2026 20:11
@gilluminate gilluminate requested review from speaker-ender and removed request for a team January 14, 2026 20:11
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Jan 14, 2026

Greptile Summary

This PR successfully refactors the automated consent flow to read all sources (GPC, migrated consent, notice consent strings) synchronously before the FidesInitialized event fires, eliminating race conditions where vendors could access stale consent values.

Key improvements:

  • Separated calculateAutomatedConsent() as a pure function and saveAutomatedPreferencesToApi() for async persistence
  • Introduced getAutomatedConsentContext() to synchronously gather all automated consent sources early in initialization
  • Applied automated consent to experience defaults and cookie before firing initialization events
  • Moved API persistence to async/non-blocking phase to prevent initialization delays
  • Created buildConsentPreferencesArray() shared utility to reduce code duplication across manual and automated consent flows
  • Updated all three initialization files (fides.ts, fides-headless.ts, fides-tcf.ts) consistently
  • Added comprehensive Cypress E2E tests for GPC, notice consent strings, and OneTrust migration scenarios

Priority handling is correct:

  1. Migrated consent (OneTrust, etc.) - highest priority, only applies if no existing Fides cookie
  2. Notice consent string (fidesString override) - second priority
  3. GPC (Global Privacy Control) - lowest priority, requires GPC flag on notice
  4. NOTICE_ONLY mechanisms are always true and cannot be overridden by any automated source (GDPR compliance)

The implementation maintains separation of concerns, follows existing patterns, and includes good test coverage.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence. The refactoring is well-structured, thoroughly tested, and solves a real synchronization problem without introducing new risks.
  • Score reflects: (1) Clean separation of concerns with pure functions for calculation and async for persistence; (2) Consistent pattern applied across all three initialization variants; (3) Comprehensive test coverage including unit tests and E2E scenarios; (4) Correct priority handling for multiple automated consent sources; (5) Proper NOTICE_ONLY protection ensuring GDPR compliance; (6) Well-documented code with clear explanations of what happens when; (7) No breaking changes to existing APIs; (8) Solves legitimate initialization race condition.
  • No files require special attention. All changes are well-implemented and thoroughly tested.

Important Files Changed

Filename Overview
clients/fides-js/src/lib/automated-consent.ts Core refactoring: Splits automated consent into two functions - calculateAutomatedConsent() as pure function and saveAutomatedPreferencesToApi() for async persistence. Properly implements priority-based consent evaluation (migrated > notice string > GPC) with correct NOTICE_ONLY handling. Good separation of concerns.
clients/fides-js/src/lib/consent-context.ts Introduces getAutomatedConsentContext() for synchronous gathering of all automated consent sources (GPC, migrated consent, notice strings). Clean implementation with proper separation from async operations. Correctly checks for existing Fides cookie to gate migrated consent application.
clients/fides-js/src/lib/initialize.ts Critical refactoring: Synchronously calculates automated consent before updating experience/cookie and firing events. Applies automated consent to experience defaults before cookie update. Asynchronously persists to API via saveAutomatedPreferencesToApi() without blocking FidesInitialized event. Proper flow ensures consent is ready when vendors access it.
clients/fides-js/src/fides.ts Entry point properly updated to call getAutomatedConsentContext() synchronously early in init. Passes context to initialize(). All three init files (fides.ts, fides-headless.ts, fides-tcf.ts) follow same pattern for consistency. Migrated consent is applied to initial cookie state.
clients/fides-js/tests/lib/automated-consent.test.ts Comprehensive test coverage for automated consent calculation with multiple scenarios (GPC, migrated consent, notice strings, priority handling, NOTICE_ONLY protection). Properly mocks dependencies and validates consent method determination.
clients/fides-js/tests/lib/shared-consent-utils.test.ts Extensive test coverage for buildConsentPreferencesArray including full/minimal objects, locale handling, NOTICE_ONLY mechanism conversion, edge cases with empty arrays and undefined values. Tests both boolean and UserConsentPreference value handling.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants