privacysandbox / privacy-sandbox-dev-support

Discuss your Privacy Sandbox developer questions with the Chrome team.
Apache License 2.0
178 stars 73 forks source link

Third Party cookies access in iframe #409

Open jaissam10 opened 3 weeks ago

jaissam10 commented 3 weeks ago

We have an extension that, when a PDF URL is loaded, inserts an iframe containing our web-accessible resource (an extension HTML page). This page then creates another iframe that loads our React application hosted on a domain, such as https://abc.com/index.html.

When a user opens a PDF URL, like https://pdfobject.com/pdf/sample.pdf, two iframes are embedded in the page—the first being our extension page and the second being our React application. These two iframes communicate with each other via postMessage.

The login process in our React application relies on cookies that are set on a different domain (e.g., subdomain.abc.com). However, these cookies are not being included in the network API call for sign-in when third-party cookies are disabled by the user or when the browser enforces third-party cookie restrictions.

Given that PDF URLs are dynamic, partitioned cookies (which are tied to the top-level site) aren't a viable solution.

How can we resolve this issue?

CC: @krgovind @samdutton

cfredric commented 2 weeks ago

Hi @jaissam10,

I'm not sure I fully understand your scenario, but the general solution when partitioned cookies/storage are not an option is to use unpartitioned storage, e.g. unpartitioned cookies. Your React app can use the Storage Access API to request access to that data.

jaissam10 commented 1 week ago

@cfredric Thanks for reaching out!

We’re unable to use the Document.requestStorageAccess() method because it prompts the user with a popup to allow or deny access. Since our URLs are dynamic for each PDF, this would trigger a popup every time, which isn’t a practical solution.

To explain further: If I open a PDF, like https://pdfobject.com/pdf/sample.pdf, the top-level site (PDF URL) is dynamic. We then insert our iframe (web_accessible_resource), i.e., (chrome-extension://id/viewer.html). Inside this, we load a nested iframe URL of CDN. So, there will be two nested iframes—one for chrome-extension://<id>/index.html and another for https://abc.com/index.html. (this is our hosted cdn)

Reference image:

image
cfredric commented 1 week ago

Since our URLs are dynamic for each PDF, this would trigger a popup every time, which isn’t a practical solution.

If I understand correctly, your CDN (https://abc.com) needs to access its unpartitioned data when embedded under some aribitrary set of top-level sites, correct? The problem is, that looks an awful lot like tracking. So the prompts are working as intended here.


However, there may still be options for you, since your app isn't just a CDN, it's also an extension. You can therefore use all of the extension APIs (e.g. chrome.cookies) to access the browser's cookies (provided you've requested the appropriate host permissions), and use message passing to send that info from your extension to the CDN iframe. Your CDN can then do whatever it needs to with those cookies, including setting partitioned cookies.

That approach is a bit more cumbersome than accessing the CDN's unpartitioned cookies directly, but as I said above, the unpartitioned cookies are intentionally inaccessible to the CDN unless the user grants permission, via the requestStorageAccess prompt or similar (soon). So, you'll have to use your extension's elevated privileges in order to get access.

jaissam10 commented 1 week ago

@cfredric Thanks for taking the time to check and follow up on this.

You're correct that our CDN (https://abc.com) needs access to unpartitioned data (cookies, storage, indexDB, cache, service worker). Additionally, when multiple PDFs are opened, the CDN's storage data should remain consistent across all tabs, which is only possible with unpartitioned storage or by ensuring it behaves that way.

Thanks, I believe the cookies issue can be resolved using chrome.cookies. For the storage issue, I’m considering using message passing between the CDN and chrome-extension://<id>/index.html, storing data in the extension’s storage, and then returning it via message passing when the PDF opens (i.e., when the CDN initializes).

However, could you help with addressing the service worker issue? Our CDN relies on a service worker that uses both cache and IndexedDB, which are currently partitioned. Do you have any suggestions on how we might solve this?

cfredric commented 1 week ago

However, could you help with addressing the service worker issue? Our CDN relies on a service worker that uses both cache and IndexedDB, which are currently partitioned. Do you have any suggestions on how we might solve this?

I'm not an expert on extensions, but are the options presented here viable for you? https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle#persist-data

jaissam10 commented 1 week ago

@cfredric I don’t think this approach will be effective, especially regarding service workers (which rely on caching via CacheStorage and IndexedDB).

I’m currently using the CacheStorage APIs, but since they are now partitioned, I’m looking for a way to move this functionality to the extension or web-accessible resources to ensure unpartitioned access. Can you assist with this, or suggest an alternative solution?

cfredric commented 1 week ago

If I understand correctly, your chrome-extension:// iframe will be unpartitioned if your extension has host permissions for the top-level site that the iframe is embedded under. You can do that in two ways:

  1. Using the chrome.permissions API to request the permission at runtime, on demand.
  2. Using the <all_urls> pattern to preemptively request host permissions for all possible sites.

Note that requesting host permissions also comes with a prompt with warnings that users have to accept/deny when your extension is installed/updated. Host permissions are very powerful (as you can see), so this prompt is also working as intended IMO.

With that in mind, I would consider whether your extension actually needs unpartitioned storage, or whether it can work using partitioned storage instead. Then you could avoid prompting users and avoid needing the powerful permissions.

jaissam10 commented 1 week ago

@cfredric My chrome-extension:// iframe remains unpartitioned, regardless of the top-level site, which is why I’m considering moving the logic and storage functionalities there. However, I’m unsure how to transition the service worker functionality to this iframe. Since service workers can only intercept requests from the same origin where they are registered, moving it to the chrome-extension:// iframe means it won’t intercept and cache APIs from my CDN, which is happening currently. Could you provide guidance on how to manage this or suggest a different approach?

cfredric commented 1 week ago

Intercepting network requests from other origins sounds like a job for chrome.declarativeNetRequest or chrome.webRequest.

jaissam10 commented 1 week ago

@cfredric A service worker has a fetch event listener (self.addEventListener("fetch", () => {})) that is triggered for API calls within the scope defined during its registration. This allows us to implement custom logic, such as checking if a request is already cached, returning the cached response if available, or making a network request and then caching the response for future use.

This behavior is not achievable with Declarative Net Request (DNR), as DNR operates more at the network level for intercepting, blocking, or modifying requests, but lacks the same flexibility for managing and serving cached responses dynamically.

Let me know if I am wrong somewhere.

jaissam10 commented 5 days ago

@cfredric @krgovind @samdutton @ilipkind Could any of you assist with this? I would greatly appreciate your feedback.

cfredric commented 4 days ago

If I understand correctly, you're asking how to use an unpartitioned service worker to intercept network requests for some origin (https://abc.com) while it's in a partitioned context.

Web platform

Web Platform APIs (on their own) definitely do not provide the ability to intercept another origin's network requests, since doing so would violate the Same Origin Policy. So any solution that bypasses the SOP would necessarily be in the extension APIs.

With that framing, we can consider alternatives that don't violate the SOP; i.e., things that your CDN can do on its own origin. The ideal answer would be to let the CDN access an unpartitioned service worker via the Storage Access API, but unpartitioned service workers are intentionally inaccessible in third-party contexts, due to security concerns. So this functionality is intentionally not possible via Web Platform APIs.

Extensions

I'm not aware of a way to do this via extension APIs. chrome.declarativeNetRequest or chrome.webRequest are the APIs that extensions can use to observe, modify, and block outgoing network requests, but I agree, those APIs don't appear to support caching -- but I am not an extensions expert.

You can consider filing a feature request for this.