Skip to content

✨ Consent: Pass consent string and metadata to amp-iframe#29636

Merged
zhouyx merged 15 commits intoampproject:masterfrom
nerdbeere:implement-consent-for-iframe
Sep 9, 2020
Merged

✨ Consent: Pass consent string and metadata to amp-iframe#29636
zhouyx merged 15 commits intoampproject:masterfrom
nerdbeere:implement-consent-for-iframe

Conversation

@nerdbeere
Copy link
Copy Markdown
Member

As discussed in #28719 it is necessary for us to retrieve the consent data within amp-iframe.

  • Implemented consent string and consent metadata forwarding to amp-iframe
  • Added documentation on how to retrieve consent data within the iframe
  • Added test page for amp-iframe with multiple CMPs
  • There are no tests yet, if this is implemented in the right spots I'm happy to add tests
  • Not implemented for amp-video-iframe because the implementation would probably look different

resolves #28719
related: #26938

@google-cla google-cla bot added the cla: yes label Aug 3, 2020
@nerdbeere nerdbeere marked this pull request as ready for review August 3, 2020 17:30
@amp-owners-bot amp-owners-bot bot requested review from estherkim and wassgha August 3, 2020 17:30
@kristoferbaxter kristoferbaxter requested review from alanorozco and zhouyx and removed request for wassgha August 5, 2020 15:42
@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Aug 6, 2020

👀

@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Aug 6, 2020

Thanks @nerdbeere for the Pull Request and my apology for the delay.

I agree with you that an API similar to send-interactions is desired. However one caveat is AMP currently doesn't support a consent update handler today. So in theory you would expect a postMessage whenever the consent changes, to be called whenever the consent changes, but in reality it will only give you the realtime consent information when send-consent-data is received.

I can think of a few approaches to address this
Option 1. In stead of supporting the send-consent-data API, have the <amp-iframe> pass in the initial consent information instead. This would be similar with the ad iframe.https://github.com/ampproject/amphtml/blob/master/3p/ampcontext.js#L349
This is preferred if you don't need following consent information update during one page visit.

Option 2. Go ahead with your current design. In future we can post all consent changes via the API.

Let me know if you think caveat we have today is fine with you, and we can proceed with option 2. Thanks.

@nerdbeere
Copy link
Copy Markdown
Member Author

nerdbeere commented Aug 6, 2020

thanks for your feedback @zhouyx!
After some consideration and discussing it with my colleagues I think it would make sense to stick to the current design for two reasons:

  1. It's closer to the current api that exists (postMessage). Passing it initially would probably mean to introduce an entirely new contract like amp context as you mentioned which I'm not sure is desired.
  2. We'd like to help with the update mechanism and implement this not only for amp-iframe but also for amp-ad and amp-video-iframe if help is needed there. In this light I think it makes sense to introduce now a future proof contract. Ideally it behaves exactly like the intersection updates as consent can be revoked as well.

If the implementation in general is fine then can you maybe tell me what we should add/do in order to get this PR merged?

Thanks in advance!

this.subscriptionApiConsent_.send(
'consent-data',
dict({
'consentMetadata': consents[0],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do you also want to pass the consentState. Asking because it's possible that no consentString or consentMetadata will be set (when there's <amp-consent> but no CMP)

Good thing about the object is that we can always add more field in the future : )


## Iframe & Consent Data

Iframes can send a `send-consent-data` message to receive consent data if a CMP is present on their parents page.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Based on the code, value will set only when data-block-on-consent attribute is added to the <amp-iframe> element. Is that expected?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point, that was a mistake on my end.

  • adjusted the test page to test with and without data-block-on-consent
  • use now policyId: default if data-block-on-consent not set
  • added some information on when to expect a consent response


this.subscriptionApiConsent_ = new SubscriptionApi(
iframe,
'send-consent-data',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: please declare the message type to MessageType within 3p-frame-messaging.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding the messageType. Could you please also add consent-data there, and use the imported the message string.


<!-- This should be in alphabetical order. _ping_ should be the first -->

<!-- _ping_ example -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think adding the fake CMP _ping_ would be sufficient to test the feature. There's no need to add following <amp-consent> elements.
In this case, you don't need to change to app.js either.

listenFor(iframe, 'embed-ready', this.activateIframe_.bind(this));
}

this.subscriptionApiConsent_ = new SubscriptionApi(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we add a unit test to cover the send-consent-data msg? Thanks

@nerdbeere
Copy link
Copy Markdown
Member Author

thank you for your in-depth review @zhouyx!
I addressed all your points, would be glad if you could re-check.

},
/*opt_is3P*/ undefined,
/*opt_includingNestedWindows*/ undefined,
/*opt_allowOpaqueOrigin*/ true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

opt_is3P=false because we don't expect sentinel to match here.
opt_includingNestedWindows=false and opt_allowOpaqueOrigin=false because nested window and opaque origin are not supported with sentinel=amp.

Feel free to not include those three optional undefined variables.


this.subscriptionApiConsent_ = new SubscriptionApi(
iframe,
'send-consent-data',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for adding the messageType. Could you please also add consent-data there, and use the imported the message string.

**Please note:**

- The `consent-data` response will only be sent once and won't update the iframe when the consent state changes (for example when the user decides to reject consent by using the post prompt ui)
- The response is delayed until the user interacts with the amp-consent ui in case iframe loading is not blocked by using `data-block-on-consent`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The response is not delayed until the user interact. It is delayed based on the data-block-on-consent value, and it's corresponding setting.
For example one can customize the consent service to resolve after a 2 seconds timeout. In this case, the response will resolve after 2 seconds, not wait for the user interaction.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thanks for clarifying!

* @param {string} origin
* @param {JsonObject} data
* @private
* @return {Promise}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: doesn't return a promise. can be removed

@nerdbeere
Copy link
Copy Markdown
Member Author

@zhouyx Thanks again for your help! Would be great if you could re-check again, ideally this would be in the next release as this is very critical for us because of the european privacy laws.

Copy link
Copy Markdown
Contributor

@zhouyx zhouyx left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks for contributing!

@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Aug 10, 2020

to @alanorozco for owner's approval.

@zhouyx zhouyx removed the request for review from estherkim August 10, 2020 23:20
@nerdbeere
Copy link
Copy Markdown
Member Author

Hi @alanorozco is something blocking this PR? Let me know if you need anything from us in order to get this merged.

Copy link
Copy Markdown
Member

@alanorozco alanorozco left a comment

Choose a reason for hiding this comment

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

LGTM with nits. Thanks!

Comment on lines +552 to +555
const consentPolicyId = super.getConsentPolicy() || 'default';
const consentStringPromise = this.getConsentString_(consentPolicyId);
const metadataPromise = this.getConsentMetadata_(consentPolicyId);
const consentPolicyStatePromise = this.getConsentPolicyState_(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: You can get rid of these intermediate methods, just call getConsentPolicyInfo, etc. directly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Answering for my colleague who is on vacation at the moment. These intermediate methods were introduced so that it is possible to write tests for this functionality (see mocking those functions here: https://github.com/ampproject/amphtml/pull/29636/files#diff-fd3e080d7fded1a7e2423b5ba744238bR945-R951). We've found similar ways to mock the unit-under-test within AMP within the test for send-intersections and embed-size. If we would call getConsentPolicyInfo directly the test would fail.

Comment on lines +281 to +293
window.addEventListener('message', function (event) {
if (
event.source != window.parent ||
event.origin == window.location.origin ||
!event.data ||
event.data.sentinel != 'amp' ||
event.data.type != 'consent-data'
) {
return;
}
console.log(event.data.consentMetadata);
console.log(event.data.consentString);
});
Copy link
Copy Markdown
Member

@alanorozco alanorozco Aug 25, 2020

Choose a reason for hiding this comment

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

For sanity I would suggest naming the short-circuit clause.

Suggested change
window.addEventListener('message', function (event) {
if (
event.source != window.parent ||
event.origin == window.location.origin ||
!event.data ||
event.data.sentinel != 'amp' ||
event.data.type != 'consent-data'
) {
return;
}
console.log(event.data.consentMetadata);
console.log(event.data.consentString);
});
function isAmpMessage(event, type) {
return event.source == window.parent &&
event.origin != window.location.origin &&
event.data &&
event.data.sentinel == 'amp' &&
event.data.type == type;
}
window.addEventListener('message', function (event) {
if (!isAmpMessage(event, 'consent-data')) {
return;
}
console.log(event.data.consentMetadata);
console.log(event.data.consentString);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Adjusted the docs and also placed the same isAmpMessage function into the send-intersections documentation block. While adjusting the docs and doing a manual test with this isAmpMessage I've found that the sentinel = amp was not send along the consent-data message and I've fixed it with this change: f6110f9

dict({
'type': MessageType.CONSENT_DATA,
'consentMetadata': consents[0],
'consentString': consents[1],
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How do we usually define data types for inbound messages? I wonder if it would make sense to have an intermediate consent object with these values.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What I've found: for embed-size the properties height and width are next to type (see https://github.com/ampproject/amphtml/blob/master/extensions/amp-iframe/0.1/amp-iframe.js#L467-L476). intersection event sends changes next to type (see https://github.com/ampproject/amphtml/blob/master/src/utils/intersection-observer-3p-host.js#L117-L120).

@google-cla
Copy link
Copy Markdown

google-cla bot commented Aug 26, 2020

All (the pull request submitter and all commit authors) CLAs are signed, but one or more commits were authored or co-authored by someone other than the pull request submitter.

We need to confirm that all authors are ok with their commits being contributed to this project. Please have them confirm that by leaving a comment that contains only @googlebot I consent. in this pull request.

Note to project maintainer: There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent. In those cases, you can manually confirm consent of the commit author(s), and set the cla label to yes (if enabled on your project).

ℹ️ Googlers: Go here for more info.

@google-cla google-cla bot added cla: no and removed cla: yes labels Aug 26, 2020
@klipstein
Copy link
Copy Markdown
Contributor

@googlebot I consent

@google-cla google-cla bot added cla: yes and removed cla: no labels Aug 26, 2020
@nerdbeere
Copy link
Copy Markdown
Member Author

@alanorozco Hi Alan, in your opinion is this ready to go now? We'd really appreciate if this gets merged soon. Thanks!

@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Sep 8, 2020

I'm going to make the ads/ui team own the 3p iframe messaging related code.
@samouri @jridgewell could one of you approve the change to src/3p-frame-messaging.js in the meanwhile, so we can merge the change. Thank you!

@nerdbeere
Copy link
Copy Markdown
Member Author

Hi @zhouyx @jridgewell any chance this can be merged now? Thanks!

@zhouyx zhouyx merged commit fb7291c into ampproject:master Sep 9, 2020
@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Sep 9, 2020

PR Merged!

@klipstein
Copy link
Copy Markdown
Contributor

Awesome! Thank you.

@muotaz
Copy link
Copy Markdown

muotaz commented Sep 15, 2020

Hi guys
When will this feature be available in the nightly or beta builds ?
Thanks,

@zhouyx
Copy link
Copy Markdown
Contributor

zhouyx commented Sep 16, 2020

Just checked, the feature should be available in nightly and beta build already.

ed-bird pushed a commit to ed-bird/amphtml that referenced this pull request Dec 10, 2020
…#29636)

* Implement consent reading for 3p iframe

* Add documentation for send-consent-data

* Fix types

* Use only _ping_ cmp on test page and revert changes in app.js

* Add MessageType to 3p-frame-messaging.js

* Allow retrieving consent without using data-block-on-consent

* Add test and some refactoring

* Fix linting in documentation

* Adjust docs

* Introduce and use MessageType and adjust jsdoc comment

* Use fixture iframe instead of srcdoc

* docs: extract isAmpMessage for receiving intersection and consent-data

* Fix for missing sentinel="amp"

Co-authored-by: Tobias von Klipstein <tobias@klpstn.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pass consent string to amp-iframe / amp-video-iframe

7 participants