Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TCF bundling based on geography #4131

Merged

Conversation

allisonking
Copy link
Contributor

@allisonking allisonking commented Sep 21, 2023

Closes #3957

Description Of Changes

We can now allow both a notice experience and a TCF experience!

Code Changes

  • Sort out some cookie logic which became much more apparently not quite right when switching between notices and TCF
  • Remove the TCF_ENABLED environment variable from the PC—now we determine if it's TCF purely from the backend 🎉
  • Cypress tests

Steps to Confirm

  • Run nox -s "fides_env(slim)" -- prod_prerelease core_version=2.20.2a2
  • Bring down the privacy center container that the test env creates
  • Bring up the privacy center on this branch using turbo dev in clients/privacy-center. I used these env variables (note: no more TCF_ENABLED 🎉 )
FIDES_PRIVACY_CENTER__DEBUG=true
FIDES_PRIVACY_CENTER__IS_OVERLAY_ENABLED=true
FIDES_PRIVACY_CENTER__IS_PREFETCH_ENABLED=true
FIDES_PRIVACY_CENTER__IS_GEOLOCATION_ENABLED=true
FIDES_PRIVACY_CENTER__GEOLOCATION_API_URL=https://cdn-api.ethyca.com/location
FIDES_PRIVACY_CENTER__PRIVACY_CENTER_URL=http://localhost:3000
  • Turn TCF on in the backend via localhost:8080/docs
  • Now that TCF is enabled, visit localhost:8080 and edit one of the existing systems.
  • Add a data use such as analytics.reporting.campaign_insights (which is one of the TCF data uses). Give it a legal basis of "Consent" and save.
  • Visit the consent page and enable all the privacy notices that are Available.
  • Visit cookie house at localhost:3000. Choose California from the geo picker. You should see "Manage preferences" show up at the bottom. Click it and you should see "Data sales and sharing". The banner won't come up since that's not required for CA
  • Now choose Frankfurt from the geo picker. The banner should come up, and you can click "manage preferences" to open what should be the TCF modal!
  • Interact with both modals, switching between geographies, to make sure your choices persist and the cookie looks right

Pre-Merge Checklist

  • All CI Pipelines Succeeded
  • Issue Requirements are Met
  • Update CHANGELOG.md

@allisonking allisonking changed the base branch from main to aking/prod-1063/tcf-prefetch September 21, 2023 20:12
@cypress
Copy link

cypress bot commented Sep 21, 2023

Passing run #4256 ↗︎

0 4 0 0 Flakiness 0
⚠️ You've recorded test results over your free plan limit.
Upgrade your plan to view test results.

Details:

Merge f2095b3 into 4b452ed...
Project: fides Commit: cb0ac18ee3 ℹ️
Status: Passed Duration: 01:15 💡
Started: Sep 21, 2023 8:42 PM Ended: Sep 21, 2023 8:44 PM

This comment has been generated by cypress-bot as a result of this project's GitHub integration settings.

@@ -38,16 +34,6 @@ interface Props {
renderModalContent: (props: RenderModalContent) => VNode;
}

/**
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realized this wasn't needed anymore once I fixed how the updating cookie logic in place worked

@@ -136,7 +141,8 @@ const updateCookie = async (
};

const tcString = await generateTcString({ tcStringPreferences, experience });
return { ...oldCookie, tc_string: tcString };
const tcfConsent = transformTcfPreferencesToCookieKeys(tcStringPreferences);
return { ...oldCookie, tc_string: tcString, tcf_consent: tcfConsent };
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably should have been in #4124 but I didn't realize it was missing until I started switching between TCF and non-TCF

@@ -252,18 +262,12 @@ export const initialize = async ({
isPrivacyExperience(effectiveExperience) &&
experienceIsValid(effectiveExperience, options)
) {
// Overwrite cookie consent with experience-based consent values
// eslint-disable-next-line no-param-reassign
cookie.consent = buildCookieConsentForExperiences(
Copy link
Contributor Author

@allisonking allisonking Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was messing up when going from Notices --> TCF because TCF doesn't have any notices, so this was making the consent object {} which would erase if someone had made choices in CA. So this logic got moved out to only notices. TCF was already reassigning the cookie, so this makes notices do the same, by having it also pass in an updateCookie func

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does have an interesting side effect which I'm not sure if we can avoid. Let's say our cookie starts as

{
  consent: {
    data_sales_and_sharing: false
  }
}

Now we switch to TCF. when TCF initializes, there are no notices, so it only has the consent defaults, which it merges with the existing cookie

{
  consent: {
    data_sales: true, 
    tracking: true, 
    analytics: true, 
    data_sales_and_sharing: true
  }
}

I'm not sure if it's ok to have the consent defaults hanging around like that. That's from this function which I think is there so that we continue support for the legacy config. We could potentially say just ignore those defaults if we are in TCF mode?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thinking beyond one impl on a customer's site, suppose a web user were to visit 2 different websites, site A uses Fides non-TCF and the site B uses Fides TCF. The user has submitted consent for site A, then navigates to site B. I'd think we'd want to preserve any existing consent the user has given on site A, rather than overwrite with defaults from TCF, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

site A and site B would use different cookies, right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't think so. It should just be the same fides_consent cookie

transformUserPreferenceToBoolean(consentPreference),
])
);
const consentCookieKey: CookieKeyConsent = Object.fromEntries(noticeMap);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

similar to https://github.com/ethyca/fides/pull/4131/files#r1333616066, this moves the cookie assignment logic out of the shared function, and into a notice-specific function. this is because otherwise, when we save TCF (which has no notices), we would overwrite cookie.consent to equal {} which isn't right!

// Update the cookie object
// eslint-disable-next-line no-param-reassign
cookie.consent = consentCookieKey;
const fidesUserPreferences: Array<ConsentOptionCreate> | undefined =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the same, but wrapped in a ternary to be a little more careful about what preferences we pass to the backend. we don't want to accidentally overwrite notice preferences when working with tcf data!

debugLog(debug, "Updating window.Fides");
window.Fides.consent = cookie.consent;
window.Fides.tc_string = cookie.tc_string;
window.Fides.tcf_consent = cookie.tcf_consent;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also probably should have been in #4124

// 2. Update the window.Fides.consent object
// 2. Update the cookie object based on new preferences
const updatedCookie = await updateCookie(cookie);
Object.assign(cookie, updatedCookie);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I learned something weird— assigning to the function param with something like

param = updatedParam;

doesn't work! (param in the parent scope is unchanged). However, doing an Object.assign does work, and so does assigning to individual attributes such as

param.consent = updatedParam.consent

@allisonking allisonking marked this pull request as ready for review September 21, 2023 21:35
Copy link
Contributor

@eastandwestwind eastandwestwind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 questions for you!

Comment on lines +101 to +103
// We determine server-side whether or not to send the TCF bundle, which is based
// on whether or not the experience is marked as TCF. This means for TCF, we *must*
// be able to prefetch the experience.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this feels like something we should log. So if the experience is TCF, and prefetch is NOT enabled in the env vars, we should log a warning or something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a bit of a chicken and egg problem, where the backend will never return that the experience is TCF if prefetch is not enabled 😕 the flow is

  1. Get the user's location
  2. Prefetch the experience, passing in the user location
  3. Check if it's TCF, which can only happen if both 1 and 2 work

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha, I've moved this suggestion here instead- https://ethyca.atlassian.net/browse/PROD-1069?focusedCommentId=36963

@@ -252,18 +262,12 @@ export const initialize = async ({
isPrivacyExperience(effectiveExperience) &&
experienceIsValid(effectiveExperience, options)
) {
// Overwrite cookie consent with experience-based consent values
// eslint-disable-next-line no-param-reassign
cookie.consent = buildCookieConsentForExperiences(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thinking beyond one impl on a customer's site, suppose a web user were to visit 2 different websites, site A uses Fides non-TCF and the site B uses Fides TCF. The user has submitted consent for site A, then navigates to site B. I'd think we'd want to preserve any existing consent the user has given on site A, rather than overwrite with defaults from TCF, right?

@allisonking allisonking merged commit 552cf68 into aking/prod-1063/tcf-prefetch Sep 28, 2023
@allisonking allisonking deleted the aking/prod-1070/tcf-geography-bundling branch September 28, 2023 15:48
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