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

Should there be a way to send topics via Fetch as a request header? #7

Closed
jkarlin opened this issue Jan 21, 2022 · 38 comments
Closed

Comments

@jkarlin
Copy link
Collaborator

jkarlin commented Jan 21, 2022

This would reduce the need for expensive (and slow) x-origin iframes to be created.

@maorithu
Copy link

maorithu commented Feb 3, 2022

Yes, I think this is very important to have. When requesting ads, our ad javascript is running on the publisher’s context which is a different origin from the one which observed the user visit various sites. For us to receive the topics, we would need to create an iframe, read the topics, and post-message them back to the publisher’s context. This introduces significant latency that is bad for user experience. Additionally, messaging the topics via the publisher's context could leak the user’s interests to parties on the publisher page, which would be a privacy step backwards relative to third-party cookies.

Ideally, topics should be sent in a way that is comparable to how cookies are sent today. Today, 3p cookies can be attached to requests as a header by:

  1. XMLHttpRequest.withCredentials = true
  2. Fetch() or WebBundle with credentials: 'include'
  3. Sent by default using an iframe

I propose we add a separate withTopics option on each interface (XHR, Fetch, iframe, WebBundle) to activate the API and attach the Topics as request headers to the ad request. It will significantly improve the ease of use of the API, reduce latency, and protect users’ privacy.

There is precedent here: this is also the approach Chrome has taken with Trust Tokens.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Feb 3, 2022

I don't think we'd want to send Topics by default, it should be explicitly called out. XHR and Fetch options seem reasonable to me.

@maorithu
Copy link

maorithu commented Feb 4, 2022

I understand we don't want to send Topics by default. For resources that are requested via iframe, what about a new iframe attribute to activate the Topic API? Similar to here and here

@jkarlin
Copy link
Collaborator Author

jkarlin commented Feb 4, 2022

Yep, an attribute could work.

@igrigorik
Copy link

Can we expose this as an opt-in feature policy extension on iframes? The opt-in provides clear signal of intent and FP enables existing tools to introspect the active policy, etc.

@zhengweiwithoutthei
Copy link

zhengweiwithoutthei commented Feb 11, 2022

Disclaimer: I am a Google Ad Manager engineer. The user 'maorithu' above is my personal account. I have a few follow up questions:

  1. Does using the Fetch/Xhr options proposed above imply a call to the API?
  2. In other words, does the caller still need to call document.browsingTopics() before using those options to send topics?
  3. If the answer to question (1) is yes, can these options be used for observing topic on a site? For example, with the option set to true, a fetch call from site A to T will make T an observer of user's visit of A and A's topic.
@jkarlin
Copy link
Collaborator Author

jkarlin commented Feb 11, 2022

This line of thinking does lead me to a question: What if the destination origin receiving topic headers does not want to receive topic headers for that user on that site? With the existing design, the API must be explicitly called via js in the context of the origin, so the origin has full control over receiving topics.

Can we expose this as an opt-in feature policy extension on iframes? The opt-in provides clear signal of intent and FP enables existing tools to introspect the active policy, etc.

There is already a permissions policy (used to be called feature policy) designed for the Topics API. Are you suggesting that if the policy is enabled, then any site covered by the permission should automatically get the header in their iframe document request headers?

Does using the Fetch/Xhr options proposed above imply a call to the API?

Yes, it would, since the topics are being received.

In other words, does the caller still need to call document.browsingTopics() before using those options to send topics?

No.

If the answer to question (1) is yes, can these options be used for observing topic on a site? For example, with the option set to true, a fetch call from site A to T will make T an observer of user's visit of A and A's topic.

Yes. I think the idea is that we'd semantically make receiving topics headers equivalent to calling the API.

@igrigorik
Copy link

There is already a permissions policy (used to be called feature policy) designed for the Topics API. Are you suggesting that if the policy is enabled, then any site covered by the permission should automatically get the header in their iframe document request headers?

Yes, I think that's one of the design options we should consider.

Can you point me to the definition of existing PP? If you are referring to browsing-topics=(), my understanding of that was as an opt-out mechanism for the top-level frame.

@zhengweiwithoutthei
Copy link

Can I assume Chrome is committed to the new API options proposed here for Xhr, fetch, iframe? When should I expect a new specs for those options to be published?

What about an "topics: 'include'" option in webbundle too(see WICG/webpackage#624 for proposed webbundle usage in ad serving)?

@jkarlin
Copy link
Collaborator Author

jkarlin commented Feb 28, 2022

Can I assume Chrome is committed to the new API options proposed here for Xhr, fetch, iframe? When should I expect a new specs for those options to be published?

I think we're going to need some way for third parties to opt-in to this behavior. Otherwise, they may receive data that they hadn't intended to be privy to via a request header.

@zhengweiwithoutthei
Copy link

Understood. I think sending topics is less of a concern as one can already send arbitrary data to a server. For setting topics, we can use a response header to indicate opt-in.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Mar 4, 2022

I think the natural fit here is client-hints. Client hints will eventually be partitioned to be per-top-level site, which we'd need. The server could signal that it wants to receive topics for this user on this site on future requests. It'd be overridden by the permission policy. If a site needs to have the topics on first seeing the user, they can use critical-ch and the request will be resent with the topics in the headers.

This would then replace the notion of using xhr/fetch/iframe options to send the topics, as it places the control of whether topics are received in the server's hands instead of whoever is calling them.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Mar 4, 2022

I'm not sure it's a perfect fit for client-hints though. We'd want a permission policy of * (the default for Topics) to mean that an accept-ch would be honored and topics sent with requests, while a permission policy of 'self' to mean that accept-ch would be registered but topics would not be sent with requests in that context if not self. I don't believe that's how client-hints usually interpret the permission policies and that could be problematic. Thoughts @yoavweiss @miketaylr ?

@zhengweiwithoutthei
Copy link

I am not sure Client Hints is a good fit here. Client Hints has a same origin policy which requires:

  • opt-ins will only be granted when the opt-in headers are received with a top-level navigation resource
  • after the opt-in, hints will only be sent with same-origin requests

This means the publisher has to change their server to opt-in and delegate hints to ad server with Feature Policy. Sending a request from the publisher's site to ad server and receiving an ACCEPT-CH response header won't register the hints.

@miketaylr
Copy link

Client hints will eventually be partitioned to be per-top-level site, which we'd need.

They can be considered to be partitioned today (but we did add a note to the spec to make that more clear).

We'd want a permission policy of * (the default for Topics) to mean that an accept-ch would be honored and topics sent with requests, while a permission policy of 'self' to mean that accept-ch would be registered but topics would not be sent with requests in that context if not self.

Just to clarify, you just don't want a topics hint to be sent by default for all requests, only if requested by an origin, right?

Client Hints indeed are defined as policy controlled features. As long as you don't want to claim that a topics hint should be included in the low-entropy hint table (which means it gets added to all requests by default, see https://wicg.github.io/client-hints-infrastructure/#request-processing), I think it could work for you.

@yoavweiss can keep me honest here.

@yoavweiss
Copy link

yoavweiss commented Mar 9, 2022

I'm not 100% clear on the data flow scenario y'all have in mind. Client Hints can be a good fit for the following:

  • publisher.com opts in to get Topics hints and delegated them with a permission policy to ad-provider.com
  • Requests to ad-provider.com then ges the Topics as a request header

In this scenario, publisher.com has all the control over the opt-in (via either Permission Policy headers or markup), and ad-provider.com has none.

One bit of control that this won't give you and e.g. a Fetch flag would is the ability to send that info on some requests for an origin, but not others. I don't know if this is important or meaningful.

If this works for y'all, then a high-entropy hint seems like a reasonable path forward. If you want ad-provider.com in the scenario above to be able to opt-in on its own, that would be a different scenario, where Client Hints may not be a good fit.

@zhengweiwithoutthei
Copy link

If you want ad-provider.com in the scenario above to be able to opt-in on its own, that would be a different scenario, where Client Hints may not be a good fit.

I think this is what we want and Client Hints doesn't seem a good fit here.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 8, 2022

Thank you for responding folks, and I apologize for my slow response. I found w3c/webappsec-permissions-policy#129 and I think I understand what's going on a bit better now.

The desired behavior is the following:

  1. By default, no party on a page will receive Topics client hint request headers.
  2. Any document or subresource request on the page or in a subframe can request to receive Topics via a Accept-CH: BrowsingTopics response header.
  3. It would be possible for the accept-ch session to outlive the browser sesssion so that the next time the user visits the page, in a new browser session, the client-hint is sent.

The Topics API is controlled by a permission policy with a default value of *. So I believe we get 1 + 2 with that right? And for 3 it sounds like callers could leverage Accept-CH-Lifetime, though I'm not sure if that is designed to outlive the browser session or not.

@jeffkaufman
Copy link

@jkarlin I'm still confused; as @zhengweiwithoutthei points out it seems like this is in conflict with https://github.com/WICG/client-hints-infrastructure#same-origin-policy

Therefore, by default, Client Hints opt-in is only valid when delivered on top-level navigation requests, and, by default, applies only to same-origin resources. Cross-origin requests must only receive hints when explicit permission is given by the first-party origin.

Where "explicit permission" means a first-party origin response header, which is something that ads (integrated via including a script) can't generally add.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 12, 2022

Unfortunately I think you're right @jeffkaufman . I had hoped that section was out-of-date since I know that permissions policy was added later to client hints. I was unaware that client hints had its own policy on top of permissions policy. So we're back to square one.

@jeffkaufman
Copy link

@jkarlin thinking about the privacy model, which I think is the same for topics as it is for client hints, it sounds like there are two main requirements:

  • Simply having a resource loaded from a page should not be enough to get you client hints. A third-party would need permission either from JavaScript or first party headers.

  • A server should not receive a client hint header unless it asked for one.

One way we could change the spec, following these constraints, which actually also makes general use of client hints better, would be to make their be a way that JavaScript could enable the header based form.

Spitballing an API, this could look like:

navigator.enableClientHintsForOrigin('https://third-party.example');

For example, the initial fetch of the ad network JavaScript could include an Accept-CH header. Today, the client would ignore that header. With this change however, after the JS called enableClientHintsForOrigin, client hints could be included on future requests to that origin.

@zhengweiwithoutthei
Copy link

zhengweiwithoutthei commented Apr 12, 2022

Simply having a resource loaded from a page should not be enough to get you client hints. A third-party would need permission either from JavaScript or first party headers.

If a third-party can run JavaScript on 1P to turn on CH for themselves, what is the difference from simply allowing 3P to use Accept-CH directly? I don't think we want either of them for the reason https://github.com/WICG/client-hints-infrastructure#same-origin-policy

Since we are back to square one, what is main concern with the proposed Fetch extension comparing to the x-iframe approach?

@jeffkaufman
Copy link

If a third-party can run JavaScript on 1P to turn on CH for themselves, what is the difference from simply allowing 3P to use Accept-CH directly?

The difference is that some third parties on a page are running JavaScript, while others are only including passive resources (img beacons etc). If you can run JavaScript, you can already run the active async APIs, and my proposal above doesn't grant any new capabilities, just speeds up existing ones.

@zhengweiwithoutthei
Copy link

zhengweiwithoutthei commented Apr 12, 2022

The current policy says without 1p opt-in and delegation, 3p won't receive the hints. There is no JS api to access the hints for 3P. The user agent client hint API is an exception. It doesn't follow the same-origin policy.
Are you suggesting we make a similar but more strict exception for Topics which allows 3P opt them in via JS to receive Topics on the subsequence requests? I think that makes more sense.

@yoavweiss
Copy link

@jeffkaufman is correct that the main goal (at least originally) of these restrictions was to avoid opt-ins made by passive resources.

When @arichiv recently added markup based delegation, we explicitly made it so that the delegation would not work when injected through script. Assuming that this is fine from a security perspective, one way to satisfy your use case could be to relax that restriction.

@zhengweiwithoutthei
Copy link

Thinking about the control model, we want 3rd parties (ad tech companies) to be able to run some Javascript code to opt-in themself (but not others) for Topics API.

This seems like a good fit for the 3P Origin Trial model, with some modifications. For 3P Origin Trial activation, a 3rd party provider run Javascript code to inject a token. The browser verifies the origin of the script who injects the token matches the origin that is encoded in the token itself and activate the feature. A valid token enables a feature for all participants on the same context. If we can make the token ONLY enabling the API for the ORIGIN it encoded, it provides a way for the site (1p) or the ad tech companies(3p) to selectively only enrolling themself for a feature. That is, if adtech.com's script injects the adtech.com's 3P OT token,

  1. with the iframe approach, the API is only exposed inside the adtech.com's context
  2. with the Fetch extension proposed in this issue, the browser only respects the option and calls the API and attaches the topics on request sent to adtech.com.

It might not be too hard to modify Origin Trial for support this extension. However, I believe this can be a good model beyond Origin Trial to provide 3P opt-in for a feature.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 26, 2022

I think it's pretty clear at this point that client-hints are not designed to support third-party opt-in to request headers. So at this point our options are:

  1. Come up with a new platform method of opting into request headers. I'm not particularly eager to go down this road.
  2. Try to encourage client-hints folks to work the way we'd need
  3. Disentangle the Topics API such that getting topics does not change browsing state (e.g., count as the destination having viewed the user on a page with that topic).

I think 3 is the best choice here, as mentioned in #54. With that option, we'd have a request header for sending topics, and a response header for recording a view of the current page's topic.

Then we'd need to add the options/attributes to fetch/iframe/xhr as proposed above.

@zhengweiwithoutthei
Copy link

Thanks Josh.
Will the proposed response header be used for opt-in the viewing or opt-out? In other words, if the response header is missing, will the view of the current page's topic happen by default? From your comment, it looks like the response header is required for count as a view.
However, the argument proposed in #54 has a default value of true, browsingTopics(add_current_topics=true), which makes this argument optional and only useful for opt-out.
Should we make both approaches consistent?

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 27, 2022

Will the proposed response header be used for opt-in the viewing or opt-out? In other words, if the response header is missing, will the view of the current page's topic happen by default? From your comment, it looks like the response header is required for count as a view.

You're correct, the response header would be required to count as a view. This ensures that the server requested the view.

However, the argument proposed in #54 has a default value of true, browsingTopics(add_current_topics=true), which makes this argument optional and only useful for opt-out.

Sure, but the script is running in the origin's iframe. Therefore the origin had a choice of registering the view or not.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 27, 2022

Just to set expectations here, I wouldn't expect a change like this to happen before Chrome M104.

@jkarlin
Copy link
Collaborator Author

jkarlin commented Apr 27, 2022

I'm still concerned about a server receiving topics if they haven't asked for it. I'd like to hear from ad-techs here. Is this a concern? I suppose one could always receive anything in a url (e.g., a sender could append anything they wanted in a query param).

@zhengweiwithoutthei
Copy link

I don't think it is an issue. The status quo is anyone can send any arbitrary data to a server without the receiving party asking for it.

The benefit of the response header approach is that it provides a way for the receiving party to opt-out the viewing part of the current topic if the request is not what they expected so the browser state can stay intact.

With the iframe approach, A's script can fetch B's iframe on C's website. It is really difficult for the iframe to tell if it is in a trusted environment and whether the API should be called to get/set topics. I feel like the browser state is more vulnerable in this situation.

@csangos
Copy link

csangos commented Jun 27, 2022

This thread covers server requests and adtech receiving topics. How are consumers choices to not send topics to a top level domain honored here?

@jeffkaufman
Copy link

@csangos That sounds like it should be a different thread? This thread is about whether the topics can be sent via header vs only from an x-origin iframe?

@jkarlin
Copy link
Collaborator Author

jkarlin commented Nov 21, 2022

Some updates on this. We're currently adding support for the headers in Fetch. We're thinking about adding support (temporarily only, e.g., during OT only) for XHR. And we're very much on the fence about supporting document requests. Document requests get a bit weird:

  1. due to random noise, it's possible to receive a topic even if you've never called the API. So I think it'll surprise some folks to receive topics by default in request headers. This isn't ideal.
  2. we don't want topics to be sent by default in document requests, it should require some sort of opt-in. We can sort-of achieve that with the existing permission policy if we make it default 'self', except same-origin sub-frames will still receive topics by default. So we could use something in addition to permission policy, like some other attribute to opt-in to, but then it gets redundant: <iframe src="..." allow="browsingTopics" browsingTopicHeaders=true> or some such.

So, if document headers is important to folks, please chime in here with some explanation as to why javascript approaches (e.g., fetch headers or iframe javascript calls) won't suffice.

@zhengweiwithoutthei @igrigorik

@dmarti
Copy link
Contributor

dmarti commented Nov 21, 2022

Document requests also get weird for Consent Management Platform (CMP) integration reasons -- if the header is only passed on requests created from JS, it's easier to check consent string (which might be set by a 3rd-party CMP on the page) before calling. Putting the header on document requests means it's harder to keep it from being sent when the user is in a no-consent, objection, or opt-out state with the site controller.

Much more manageable for the site if it is Fetch and/or XHR only.

@xyaoinum xyaoinum mentioned this issue Nov 30, 2022
aarongable pushed a commit to chromium/chromium that referenced this issue Jan 11, 2023
Add support for XHR (temporarily only, e.g., during OT only) per
discussion in:
patcg-individual-drafts/topics#7 (comment)

- Use a separate flag so that we can ship Topics without shipping XHR
support.
- Improve/fix the checks in ResolveInvalidConfigurations(): drop the
check for kPrivacySandboxAdsAPIsOverride, as an inconsistent configuration can occur via
"--enable-blink-features=TopicsAPI --disable-features=BrowsingTopics"
as well, and it's insufficient to only scope to when
kPrivacySandboxAdsAPIsOverride is enabled.

Bug: 1400744
Change-Id: Id3b31d8c60e69c4f4b6d88ba34107a7ae399e69a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4103742
Reviewed-by: John Delaney <johnidel@chromium.org>
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Josh Karlin <jkarlin@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1091402}
aarongable pushed a commit to chromium/chromium that referenced this issue Jan 11, 2023
This reverts commit 08389ed.

Reason for revert: cause build failure:https://ci.chromium.org/ui/p/chromium/builders/ci/Cast%20Audio%20Linux/156481/overview 

Original change's description:
> [Topics] implement browsingTopics for XHR
>
> Add support for XHR (temporarily only, e.g., during OT only) per
> discussion in:
> patcg-individual-drafts/topics#7 (comment)
>
> - Use a separate flag so that we can ship Topics without shipping XHR
> support.
> - Improve/fix the checks in ResolveInvalidConfigurations(): drop the
> check for kPrivacySandboxAdsAPIsOverride, as an inconsistent configuration can occur via
> "--enable-blink-features=TopicsAPI --disable-features=BrowsingTopics"
> as well, and it's insufficient to only scope to when
> kPrivacySandboxAdsAPIsOverride is enabled.
>
> Bug: 1400744
> Change-Id: Id3b31d8c60e69c4f4b6d88ba34107a7ae399e69a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4103742
> Reviewed-by: John Delaney <johnidel@chromium.org>
> Commit-Queue: Yao Xiao <yaoxia@chromium.org>
> Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
> Reviewed-by: Josh Karlin <jkarlin@chromium.org>
> Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1091402}

Bug: 1400744
Change-Id: I64354a391993c64654984ca4b7ea5fc8971bc5d5
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4156735
Reviewed-by: Tom Lukaszewicz <tluk@google.com>
Owners-Override: Tom Lukaszewicz <tluk@google.com>
Reviewed-by: Yao Xiao <yaoxia@chromium.org>
Commit-Queue: Victor Tan <victortan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1091422}
aarongable pushed a commit to chromium/chromium that referenced this issue Jan 12, 2023
This is a reland of commit 08389ed

The revert was due to stale generated files / not related to this patch: https://bugs.chromium.org/p/chromium/issues/detail?id=1406629


Original change's description:
> [Topics] implement browsingTopics for XHR
>
> Add support for XHR (temporarily only, e.g., during OT only) per
> discussion in:
> patcg-individual-drafts/topics#7 (comment)
>
> - Use a separate flag so that we can ship Topics without shipping XHR
> support.
> - Improve/fix the checks in ResolveInvalidConfigurations(): drop the
> check for kPrivacySandboxAdsAPIsOverride, as an inconsistent configuration can occur via
> "--enable-blink-features=TopicsAPI --disable-features=BrowsingTopics"
> as well, and it's insufficient to only scope to when
> kPrivacySandboxAdsAPIsOverride is enabled.
>
> Bug: 1400744
> Change-Id: Id3b31d8c60e69c4f4b6d88ba34107a7ae399e69a
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4103742
> Reviewed-by: John Delaney <johnidel@chromium.org>
> Commit-Queue: Yao Xiao <yaoxia@chromium.org>
> Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
> Reviewed-by: Josh Karlin <jkarlin@chromium.org>
> Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1091402}

Bug: 1400744
Change-Id: Ica428e54e53237fe54c6909519a50cdadd62e8f8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4158731
Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Commit-Queue: Yao Xiao <yaoxia@chromium.org>
Reviewed-by: Josh Karlin <jkarlin@chromium.org>
Reviewed-by: John Delaney <johnidel@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1091980}
@jkarlin
Copy link
Collaborator Author

jkarlin commented Mar 27, 2023

Hey folks. We've added support for topics in document request headers for iframes only, and an attribute has to be set for them to be sent. See #145 for details.

edit: Forgot to add the reason! We're quite concerned with the performance of Topics, and supporting sending topics with an ad request loaded directly into a document seems like a super efficient proposal to support. We required the attribute to ensure that Topics are only sent when requested to be.

@jkarlin jkarlin closed this as completed Mar 27, 2023
@zhengweiwithoutthei
Copy link

Thanks @jkarlin for the update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
9 participants