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

Add notice consent to Fides string #5895

Open
wants to merge 16 commits into
base: main
Choose a base branch
from

Conversation

gilluminate
Copy link
Contributor

@gilluminate gilluminate commented Mar 15, 2025

Closes LJ-305

Description Of Changes

Introduces Notice Consent String functionality to the FidesJS client, allowing programmatic control of notice consent preferences through a base64-encoded string. The changes include:

  • New encoding/decoding utilities for Notice Consent strings
  • Integration with the existing Fides string format in a new fourth slot
  • Automated application of Notice Consent preferences during initialization

Code Changes

  • Added encodeNoticeConsentString and decodeNoticeConsentString utility functions and exposed encodeNoticeConsentString to the Fides window object.
  • Updated DecodedFidesString interface to include noticeConsent field
  • Modified initialize to handle Notice Consent string application
  • Removed unused context parameter from resolveConsentValue
  • Added comprehensive tests for Notice Consent string encoding/decoding

Steps to Confirm

  1. In Admin UI set up a Banner, a Banner/Modal, and a TCF experience with various consent notices.
  2. Load the demo page and use the provided window.Fides.encodeNoticeConsentString() to get a Notice Consent String. Use the keys for the consent notices configured in step 1. Get the encoded string for various combinations of true/false values for those.
  3. Revisit the demo page with the fides_string=... query string, and use each encoded Notice Consent string noted above as the value in the fourth slot of the comma separated Fides String (eg. fides_string=,,,eyJkYXRhX3NhbGVzX2FuZF9zaGFyaW5nIjowfQ==)
  4. Ensure that
  • ...the banner never shows when the fides_string is passed in for the banner/modal experience (Note: TCF banner should still show because TCF consent still needs to be asked for (only custom purposes have been set)
  • ...the preferences are automatically set as expected. Open the modal using the modal link to ensure the same preferences were saved (toggle switches match)
  • ...the cookie consent value is correct and the consentMethod should be "script"
  • ...the payload of the api/v1/privacy-preferences endpoint is correct.

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
  • Followup issues:
    • No followup issues
  • Database migrations:
    • No migrations
  • Documentation:
    • Documentation added for new Notice Consent string functionality

Copy link

vercel bot commented Mar 15, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
fides-plus-nightly ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 20, 2025 7:41pm
fides-privacy-center ✅ Ready (Inspect) Visit Preview 💬 Add feedback Mar 20, 2025 7:41pm

@gilluminate gilluminate marked this pull request as draft March 15, 2025 00:57
@gilluminate gilluminate force-pushed the gill/LJ-305/add-janus-to-fides-string branch from 2eb6843 to e280bcf Compare March 17, 2025 17:45
@gilluminate gilluminate force-pushed the gill/LJ-305/add-janus-to-fides-string branch from fefe384 to b714d57 Compare March 17, 2025 18:00
@gilluminate gilluminate force-pushed the gill/LJ-305/add-janus-to-fides-string branch from 3cb239f to 69574de Compare March 17, 2025 18:18
@gilluminate gilluminate requested review from eastandwestwind and removed request for eastandwestwind March 18, 2025 02:40
@gilluminate gilluminate force-pushed the gill/LJ-305/add-janus-to-fides-string branch from 78268ad to 771e970 Compare March 18, 2025 23:08
@gilluminate gilluminate marked this pull request as ready for review March 18, 2025 23:24
@gilluminate gilluminate force-pushed the gill/LJ-305/add-janus-to-fides-string branch from 771e970 to 53af48c Compare March 18, 2025 23:25
});
});

describe("shouldResurfaceBanner", () => {
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 method already existed, but I enhanced it quite a bit and used Cursor to write these tests.

Comment on lines -86 to -90
Note: The AC_STRING can only exist if TC_STRING exists, as it's derived from it.
When GPP is enabled, the GPP_STRING portion is automatically initialized during
FidesJS initialization, either preserving any existing GPP string or using a
default value. The GPP_STRING is independent and can exist with or without the
other strings.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved a lot of the docs for setting the string to the FidesOptions docs since that's more appropriate. These docs here are mostly for reading the string.

@@ -16,7 +16,7 @@ const GZIP_SIZE_ERROR_KB = 45; // fail build if bundle size exceeds this
const GZIP_SIZE_WARN_KB = 35; // log a warning if bundle size exceeds this

// TCF
const GZIP_SIZE_TCF_ERROR_KB = 87;
const GZIP_SIZE_TCF_ERROR_KB = 90;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

we're not at this max size yet, but we're getting close to it. Seemed appropriate to keep this more general rather than bumping it with every edit.

[cookie, savedConsent, experience, options, isAutomatedConsent],
);
const showBanner = useMemo(() => {
return shouldResurfaceBanner(experience, cookie, savedConsent, options);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

all of the boolean logic was moved in to the method for reuse and stability.

propertyId,
}) => {
const { i18n, currentLocale, setCurrentLocale } = useI18n();
const parsedCookie: FidesCookie | undefined = getFidesConsentCookie();
const savedConsent = window.Fides.saved_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.

@eastandwestwind I made sure the OT migration tests still pass with this change, but with the passed in savedConsent it was missing any GPC updates that happened between initialization and the overlay updating here. As you may recall, we run the GPC logic asynchronously after initialize() which is where the params on this component get set.

if (!this.cookie) {
throw new Error("Should have a cookie");
}
return shouldResurfaceConsent(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

moved this logic in to the method as well

export const shouldResurfaceConsent = (
experience: PrivacyExperience | PrivacyExperienceMinimal,
cookie: FidesCookie,
export const shouldResurfaceBanner = (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed for clarity

Comment on lines -245 to -248
// TODO (PROD-1792): we should *also* resurface in the special case where the
// saved consent is only recorded with a consentMethod of "dismiss"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

since I was adding logic for GPC, I knocked this TODO out at the same time :)

consent: NoticeConsent | undefined,
): boolean => {
if (notice.consent_mechanism === ConsentMechanism.NOTICE_ONLY) {
return true;
}
// Note about GPC - consent has already applied to the cookie at this point, so we can trust preference there
// DEFER (PROD-1780): delete context arg for safety
Copy link
Contributor Author

Choose a reason for hiding this comment

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

confirmed this was no longer used

@@ -209,7 +209,8 @@ export const fidesStringToConsent = ({
fidesString,
cmpApi,
}: FidesStringToConsentArgs) => {
if (!fidesString || !cmpApi) {
const { gpp: gppString }: DecodedFidesString = decodeFidesString(fidesString);
if (!fidesString || !gppString || !cmpApi) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Minor optimization here, no reason to continue decoding the GPP string if one wasn't passed in.

@@ -258,8 +258,17 @@ const NoticeOverlay: FunctionComponent<OverlayProps> = ({
}, [cookie, options.debug]);

const handleDismiss = useCallback(() => {
handleUpdatePreferences(ConsentMethod.DISMISS, initialEnabledNoticeKeys());
}, [handleUpdatePreferences, initialEnabledNoticeKeys]);
if (!consentCookieObjHasSomeConsentSet(parsedCookie?.consent)) {
Copy link
Contributor Author

@gilluminate gilluminate Mar 19, 2025

Choose a reason for hiding this comment

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

This addressed a bug where if someone returns to the page, loads the modal from the link, and then closes the modal, their previous consent method was changed to dismissed even though no changes were made.

@@ -460,8 +466,10 @@ export const TcfOverlay = ({
}, [cookie, options.debug]);

const handleDismiss = useCallback(() => {
handleUpdateAllPreferences(ConsentMethod.DISMISS, draftIds);
}, [handleUpdateAllPreferences, draftIds]);
if (!consentCookieObjHasSomeConsentSet(parsedCookie?.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.

same bug as above, but on the TCF side

#### Example

```ts
const encoded = Fides.encodeNoticeConsentString({data_sales_and_sharing:0,analytics:1});
Copy link
Contributor

Choose a reason for hiding this comment

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

ohhh this is so handy, thanks for adding this!

* @param base64String The base64 encoded Notice Consent string
* @returns Decoded consent data object or null if decoding fails
*/
export const decodeNoticeConsentString = (
Copy link
Contributor

Choose a reason for hiding this comment

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

can we also expose this on the Fides obj? I can see it might be a handy util for testing and onboarding customers

const [tc = "", ac = "", gpp = "", nc = ""] =
fidesString.split(FIDES_SEPARATOR);
// If there's no TC, remove AC
return tc ? { tc, ac, gpp, nc } : { tc: "", ac: "", gpp, nc };
Copy link
Contributor

Choose a reason for hiding this comment

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

There's the formatFidesStringWithGpp method in this file- I think we still need to support nc within this too

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